Tcl Bazel rules

Rules

tcl_binary

load("@rules_tcl//tcl:defs.bzl", "tcl_binary")

tcl_binary(name, deps, srcs, data, env, main)

A tcl_binary is an executable Tcl program consisting of a collection of .tcl or .do source files (possibly belonging to other tcl_library rules), a *.runfiles directory tree containing all the code and data needed by the program at run-time, and a stub script that starts up the program with the correct initial environment and data.

load("@rules_tcl//tcl:defs.bzl", "tcl_binary")

tcl_binary(
    name = "foo",
    srcs = ["foo.tcl"],
    deps = [
        ":bar",  # a tcl_library
    ],
)

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
depsOther Tcl packages to link to the current target.List of labelsoptional[]
srcsThe list of source (.tcl or .do) files that are processed to create the target.List of labelsrequired
dataFiles needed by this rule at runtime. May list file or rule targets. Generally allows any target.List of labelsoptional[]
envDictionary of strings; values are subject to $(location) and "Make variable" substitution.Dictionary: String -> Stringoptional{}
mainThe name of the source file that is the main entry point of the application. This file must also be listed in srcs. If left unspecified, name is used instead. If name does not match any filename in srcs, main must be specified.LabeloptionalNone

tcl_library

load("@rules_tcl//tcl:defs.bzl", "tcl_library")

tcl_library(name, deps, srcs, data)

A Tcl library that can be depended upon by other Tcl targets.

A tcl_library groups a set of .tcl or .do source files that can be consumed by other Tcl targets. If a pkgIndex.tcl file is included in srcs, its directory is added to the consumer's auto_path so the package can be loaded with Tcl's package require command. Libraries without a pkgIndex.tcl are still made available through runfiles and can be loaded directly with source (typically via the runfiles library's rlocation).

Example with package require:

load("@rules_tcl//tcl:defs.bzl", "tcl_library")

tcl_library(
    name = "mylib",
    srcs = [
        "mylib.tcl",
        "pkgIndex.tcl",
    ],
    deps = [
        ":otherlib",  # Another tcl_library
    ],
    visibility = ["//visibility:public"],
)

The library can then be used as a dependency in other targets:

tcl_binary(
    name = "app",
    srcs = ["app.tcl"],
    deps = [":mylib"],
)

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
depsOther Tcl packages to link to the current target.List of labelsoptional[]
srcsThe list of source (.tcl or .do) files that are processed to create the target.List of labelsrequired
dataFiles needed by this rule at runtime. May list file or rule targets. Generally allows any target.List of labelsoptional[]

tcl_toolchain

load("@rules_tcl//tcl:defs.bzl", "tcl_toolchain")

tcl_toolchain(name, tclcore, tcllib, tclsh, wrapper_entrypoint, wrapper_template)

A toolchain rule that defines the Tcl interpreter, libraries, and executable wrapper used to build tcl_binary, tcl_library, and tcl_test targets.

Registering a toolchain

rules_tcl does not register a toolchain for you. Add the following to your MODULE.bazel to use the default (a stock tclsh and tcllib, with the built-in shell/batch wrapper):

register_toolchains("@rules_tcl//tcl/toolchain")

If you need a custom toolchain (e.g. a different Tcl version, or a wrapper that dispatches into a different host interpreter), define your own. tclcore and tcllib are matched by provider — pass a target that returns TclCoreInfo / TclLibInfo, typically produced by the sibling tclcore_filegroup and tcllib_filegroup rules:

load("@rules_tcl//tcl:tcl_toolchain.bzl", "tcl_toolchain", "tclcore_filegroup")

tclcore_filegroup(
    name = "my_tclcore",
    srcs = ["@my_tcl//:tclcore_files"],
)

tcl_toolchain(
    name = "my_tcl_toolchain",
    tclsh = "@my_tcl//:tclsh",
    tclcore = ":my_tclcore",     # optional; must provide TclCoreInfo
    tcllib = "@tcllib",          # optional; must provide TclLibInfo
    wrapper_template = "//path/to:my_wrapper_bundle",
    wrapper_entrypoint = "//path/to:my_entrypoint.tcl",  # optional
)

tclcore and tcllib may be omitted when the interpreter ships them itself. wrapper_entrypoint is optional when the wrapper does not need a bootstrap script.

The template's own DefaultInfo.default_runfiles are merged into every produced binary, so a template that needs a helper library (e.g. @rules_shell//shell/runfiles for sh, @rules_batch//batch/runfiles for batch) should be declared via a filegroup that lists the helper under data:

filegroup(
    name = "my_wrapper_bundle",
    srcs = ["my_wrapper.sh.tpl"],
    data = ["@rules_shell//shell/runfiles"],
)

The same is true for wrapper_entrypoint — a filegroup around the entrypoint can carry extra runtime data it needs at execution.

The extension appended to the produced wrapper is derived from the template's filename by splitting the basename on the first . and dropping any trailing parts that are known template suffixes (.tpl, .template, .tmpl, .in); whatever remains is the extension. E.g. my_wrapper.sh.tpl yields .sh, my_wrapper.bat.template yields .bat, my_wrapper.vhook.tcl.tpl yields .vhook.tcl. Since the split happens at the FIRST dot, templates whose basename contains a dot in the name portion (e.g. com.foo.wrapper.sh.tpl intending .sh) should be renamed so the first . marks the extension boundary.

ToolchainInfo contract

The rule returns a platform_common.ToolchainInfo with the following fields. Non-underscore fields are the public contract that consumers (including third-party rules that operate over Tcl targets) may read. Underscore-prefixed fields are internal to the built-in tcl_binary / tcl_test implementation — third-party rules that want a materially different wrapper should carry their own template rather than reach into them.

FieldTypeAccessNotes
tclshFilepublicExecutable of the interpreter that will run the produced binaries.
includesdepset[str]publicRunfiles-relative include paths implicitly on auto_path.
init_tclFile | Nonepublicinit.tcl from tclcore, or None.
tcllib_pkg_indexFile | Nonepublictcllib's top-level pkgIndex.tcl, or None.
all_filesdepset[File]publicRuntime files merged into every produced target.
make_variable_infoplatform_common.TemplateVariableInfopublicExposes $(TCLSH) for env on downstream rules.
_wrapper_*(various)internalWrapper template, entrypoint, and derived extension.

Wrapper template contract

The built-in tcl_binary / tcl_test expand wrapper_template once per target into a file named <target><wrapper_extension> and set it as the target's executable. The template supports these substitutions; values whose backing toolchain field is unset expand to the empty string, so a template can simply omit lines it does not need:

SubstitutionValueEmpty when
{interpreter}Runfiles path (rlocation) of tclsh.never
{entrypoint}Runfiles path of wrapper_entrypoint.wrapper_entrypoint unset
{config}Runfiles path of a per-target JSON config (see below).never
{main}Runfiles path of the target's entry .tcl (resolved from main / srcs).never
{init_tcl}Runfiles path of tclcore's init.tcl.tclcore unset
{tcllib_pkg_index}Runfiles path of tcllib's top-level pkgIndex.tcl.tcllib unset
{auto_path}Tcl-list literal (brace-quoted) of every include path visible to the target — the target's own workspace, each dep's TclInfo.includes, and the toolchain's includes. Suitable to lappend onto auto_path.never

The {config} file is JSON with this shape:

{
  "includes": ["workspace_name", "workspace_name/path/to/lib", "..."],
  "runfiles": ["workspace_name/path/to/file", "..."]
}

includes is the same set of runfiles-relative paths surfaced through {auto_path}; runfiles is every file merged into the target's runfiles. The default entrypoint.tcl consumes this to set TCLLIBPATH and to materialize a RUNFILES_DIR when only a manifest is available; alternate wrappers can consume, ignore, or extend it as they see fit.

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
tclcoreA target providing TclCoreInfo (typically a tclcore_filegroup).LabeloptionalNone
tcllibA target providing TclLibInfo (typically a tcllib_filegroup).LabeloptionalNone
tclshThe path to a tclsh binary. Runtime dependency of every produced tcl_binary / tcl_test.Labelrequired
wrapper_entrypointOptional .tcl file added to the binary's runfiles and referenced via the {entrypoint} template substitution. The target's default_runfiles are also merged in. Leave unset when the wrapper doesn't need a bootstrap.LabeloptionalNone
wrapper_templateTemplate expanded per tcl_binary / tcl_test. The target's default_runfiles are merged into every produced binary, so a filegroup wrapping the template can list helper libraries under data (e.g. @bazel_tools//tools/bash/runfiles). See the wrapper template contract in the rule docs for the supported substitutions.Labelrequired