CLI

The pkl command-line interface (CLI) evaluates Pkl modules and writes their output to the console or a file. For interactive development, the CLI includes a Read-Eval-Print Loop (REPL).

Installation

The CLI comes in multiple flavors:

  • Native macOS executable for amd64 (tested on macOS 10.15)

  • Native Linux executable for amd64

  • Native Linux executable for aarch64

  • Native Alpine Linux executable for amd64 (cross-compiled and tested on Oracle Linux 8)

  • Native Windows executable for amd64 (tested on Windows Server 2022)

  • Java executable (tested with Java 17/21 on macOS and Oracle Linux)

On macOS, Linux, and Windows, we recommend using the native executables. They are self-contained, start up instantly, and run complex Pkl code much faster than the Java executable.

What is the Difference Between the Linux and Alpine Linux Executables?

The Linux executable is dynamically linked against glibc and libstdc++, whereas, the Alpine Linux executable is statically linked against musl libc and libstdc++.

The Java executable works on multiple platforms and has a smaller binary size than the native executables. However, it requires a Java 17 (or higher) runtime on the system path, has a noticeable startup delay, and runs complex Pkl code slower than the native executables.

All flavors are built from the same codebase and undergo the same automated testing. Except where noted otherwise, the rest of this page discusses the native executables.

Homebrew

On macOS and Linux, release versions can be installed with Homebrew.

To install Pkl, run:

brew install pkl

To update Pkl, run:

brew update
brew upgrade pkl   # or just `brew upgrade`

Download

Development and release versions can be downloaded and installed manually.

macOS Executable

On aarch64:

curl -L -o pkl 'https://github.com/apple/pkl/releases/download/0.27.0/pkl-macos-aarch64'
chmod +x pkl
./pkl --version

On amd64:

curl -L -o pkl 'https://github.com/apple/pkl/releases/download/0.27.0/pkl-macos-amd64'
chmod +x pkl
./pkl --version

This should print something similar to:

Pkl 0.27.0 (macOS, native)

Linux Executable

The Linux executable is dynamically linked against glibc and libstdc++ for the amd64 and aarch64 architectures. For a statically linked executable, see Alpine Linux Executable.

On aarch64:

curl -L -o pkl 'https://github.com/apple/pkl/releases/download/0.27.0/pkl-linux-aarch64'
chmod +x pkl
./pkl --version

On amd64:

curl -L -o pkl 'https://github.com/apple/pkl/releases/download/0.27.0/pkl-linux-amd64'
chmod +x pkl
./pkl --version

This should print something similar to:

Pkl 0.27.0 (Linux, native)

Alpine Linux Executable

The Alpine Linux executable is statically linked against musl libc and libstdc++. For a dynamically linked executable, see Linux Executable.

curl -L -o pkl 'https://github.com/apple/pkl/releases/download/0.27.0/pkl-alpine-linux-amd64'
chmod +x pkl
./pkl --version

This should print something similar to:

Pkl 0.27.0 (Linux, native)
We currently do not support the aarch64 architecture for Alpine Linux.

Windows Executable

Invoke-WebRequest 'https://github.com/apple/pkl/releases/download/0.27.0/pkl-windows-amd64.exe' -OutFile pkl.exe
.\pkl --version

This should print something similar to:

Pkl 0.27.0 (Windows 10.0, native)
We currently do not support the aarch64 architecture for Windows.

Java Executable

curl -L -o jpkl 'https://repo1.maven.org/maven2/org/pkl-lang/pkl-cli-java/0.27.0/pkl-cli-java-0.27.0.jar'
chmod +x jpkl
./jpkl --version

This should print something similar to:

Pkl 0.27.0 (macOS 14.2, Java 17.0.10)
The Java executable does not work as an executable file on Windows. However, it will work as a jar, for example, with java -jar jpkl.

Usage

Synopsis: pkl <subcommand> [<options>] [<args>]

For a brief description of available options, run pkl -h.

The Java executable is named jpkl.

pkl eval

Synopsis: pkl eval [<options>] [<modules>]

Evaluate the given Pkl <modules> and produce their rendering results.

<modules>

The absolute or relative URIs of the modules to evaluate. Relative URIs are resolved against the working directory.

Options

-f, --format

Default: (none)
Example: yaml
The output format to generate. The default output renderer for a module supports the following formats:

  • json

  • jsonnet

  • pcf

  • plist

  • properties

  • textproto

  • xml

  • yaml

If no format is set, the default renderer chooses pcf.

-o, --output-path

Default: (none)
Example: "config.yaml"
The file path where the output file is placed. Relative paths are resolved against the project directory.

This option is mutually exclusive with --multiple-file-output-path. If neither option is set, each module’s output.text is written to standard output.

If multiple source modules are given, placeholders can be used to map them to different output files. The following placeholders are supported:

%{moduleDir}

The directory path of the module, relative to the working directory. Only available when evaluating file-based modules.

%{moduleName}

The simple module name as inferred from the module URI. For hierarchical URIs such as file:///foo/bar/baz.pkl, this is the last path segment without file extension.

%{outputFormat}

The requested output format. Only available if --format is set.

If multiple source modules are mapped to the same output file, their outputs are concatenated. By default, module outputs are separated with ---, as in a YAML stream. The separator can be customized using the --module-output-separator option.

--module-output-separator

Default: --- (as in a YAML stream)
The separator to use when multiple module outputs are written to the same file, or to standard output.

-m, --multiple-file-output-path

Default: (none)
Example: "output/"
The directory where a module’s output files are placed.

Setting this option causes Pkl to evaluate a module’s output.files property and write the files specified therein. Within output.files, a key determines a file’s path relative to --multiple-file-output-path, and a value determines the file’s contents.

This option cannot be used together with any of the following:

This option supports the same placeholders as --output-path.

Examples:

# Write files to `output/`
pkl eval -m output/ myFiles.pkl

# Write files to the current working directory
pkl eval -m . myFiles.pkl

# Write foo.pkl's files to the `foo` directory, and bar.pkl's files
# to the `bar` directory
pkl eval -m "%{moduleName}" foo.pkl bar.pkl

For additional details, see Multiple File Output in the language reference.

-x, --expression

Default: (none)
The expression to be evaluated within the module.

This option causes Pkl to evaluate the provided expression instead of the module’s output.text or output.files properties. The resulting value is then stringified, and written to either standard out, or the designated output file.

For example, consider the following Pkl module:

pigeon.pkl
metadata {
  species = "Pigeon"
}

The following command prints Pigeon to the console:

pkl eval -x metadata.species pigeon.pkl
# => Pigeon

Setting an --expression flag can be thought of as substituting the expression in place of a module’s output.text property. Running the previous command is conceptually the same as if the below module were evaluated without the --expression flag:

metadata {
  species = "Pigeon"
}

output {
  text = metadata.species.toString()
}

This command also takes common options.

pkl server

Synopsis: pkl server

Run as a server that communicates over standard input/output.

This option is used for embedding Pkl in an external client, such as pkl-swift or pkl-go.

pkl test

Synopsis: pkl test [<options>] [<modules>]

Evaluate the given <modules> as tests, producing a test report and appropriate exit code.

Renderers defined in test files will be ignored by the test command.

Tests that result in writing pkl-expected.pcf files are considered failing tests. If these are the only failures, the command exits with exit code 10. Otherwise, failures result in exit code 1.

<modules>

The absolute or relative URIs of the modules to test. Relative URIs are resolved against the working directory.

Options

--junit-reports

Default: (none)
Example: ./build/test-results
Directory where to store JUnit reports.

No JUnit reports will be generated if this option is not present.

--overwrite

Force generation of expected examples.
The old expected files will be deleted if present.

This command also takes common options.

pkl repl

Synopsis: pkl repl [<options>]

Start a REPL session.

This command takes common options.

pkl project package

Synopsis: pkl project package <project-dir>

This command prepares a project to be published as a package. Given a project directory, it creates the following artifacts:

  • <name>@<version> - the package metadata file

  • <name>@<version>.sha256 - the dependency metadata file’s SHA-256 checksum

  • <name>@<version>.zip - the package archive

  • <name>@<version>.zip.sha256 - the package archive’s SHA-256 checksum

These artifacts are expected to be published to an HTTPS server, such that the metadata and zip files can be fetched at their expected locations.

The package ZIP should be available at the packageZipUrl location specified in the PklProject file The package metadata should be available at the package URI’s derived HTTPS URL. For example, given package package://example.com/mypackage@1.0.0, the metadata file should be published to https://example.com/mypackage@1.0.0.

During packaging, this command runs these additional steps:

  1. Run the package’s API tests, if any are defined.

  2. Validates that if the package has already been published, that the package’s metadata is identical. This step can be skipped using the --skip-publish-check flag.

Examples:

# Search the current working directory for a project, and package it.
pkl project package

# Package all projects within the `packages/` directory to `.out`, writing each package's artifacts to its own directory.
pkl project package --output-path ".out/%{name}@%{version}/" packages/*/

Options

--output-path

Default: .out

The directory to write artifacts to. Accepts the following placeholders:

%{name}

The name of the package

%{version}

The version of the package

--skip-publish-check

Skips checking whether a package has already been published with different contents.

By default, the packager will check whether a package at the same version has already been published. If the package has been published, it validates that the package’s metadata is identical to the locally generated metadata.

--junit-reports

Default: (none)
Example: ./build/test-results
Directory where to store JUnit reports.

No JUnit reports will be generated if this option is not present.

--overwrite

Force generation of expected examples.
The old expected files will be deleted if present.

This command also takes common options.

pkl project resolve

Synopsis: pkl project resolve <project-dir>

This command takes the dependencies of a project, and writes the resolved versions a file at path PklProject.deps.json.

It builds a dependency list, taking the latest minor version in case of version conflicts. For more details, see the resolving dependencies section of the language reference.

Examples:

# Search the current working directory for a project, and resolve its dependencies.
pkl project resolve

# Resolve dependencies for all projects within the `packages/` directory.
pkl project resolve packages/*/

Options

This command accepts common options.

pkl download-package

Synopsis: pkl download-package <package-uri>

This command downloads the specified packages to the cache directory. If the package already exists in the cache directory, this command is a no-op.

Options

This command accepts common options.

pkl analyze imports

Synopsis: pkl analyze imports [<modules>]

This command builds a graph of imports declared in the provided modules.

This is a lower level command that is meant to be useful for Pkl-related tooling. For example, this command feeds into the Gradle Plugin to determine if tasks are considered up-to-date or not.

This command produces an object with two properties, imports and resolvedImports.

The imports property is a mapping of a module’s absolute URI, to the set of imports declared within that module.

The resolvedImports property is a mapping of a module’s absolute URI (as stated in imports), to the resolved absolute URI that might be useful for fetching the module’s contents. For example, a local dependency import will have an in-language URI with scheme projectpackage:, and may have resolved URI with scheme file: (assuming that the project is file-based).

Examples:

# Analyze the imports of a single module
pkl analyze imports myModule.pkl

# Same as the previous command, but output in JSON.
pkl analyze imports -f json myModule.pkl

# Analyze imports of all modules declared within src/
pkl analyze imports src/*.pkl
<modules>

The absolute or relative URIs of the modules to analyze. Relative URIs are resolved against the working directory.

Options

-f, --format

Same meaning as -f, --format in pkl eval.

-o, --output-path

Same meaning as -o, --output-path in pkl eval.

This command also takes common options.

Common options

The pkl eval, pkl test, pkl repl, pkl project resolve, pkl project package, pkl download-package, and pkl analyze imports commands support the following common options:

--allowed-modules

Default: pkl:,file:,modulepath:,https:,repl:,package:,projectpackage:
Comma-separated list of URI patterns that determine which modules can be loaded and evaluated. Patterns are matched against the beginning of module URIs. (File paths have been converted to file: URLs at this stage.) At least one pattern needs to match for a module to be loadable.

--allowed-resources

Default: env:,prop:,package:,projectpackage:
Comma-separated list of URI patterns that determine which external resources can be read. Patterns are matched against the beginning of resource URIs. At least one pattern needs to match for a resource to be readable.

--color

Default: auto
When to format messages with ANSI color codes. Possible values:

  • "never": Never format

  • "auto": Format if stdin, stdout, or stderr are connected to a console.

  • "always": Always format

--cache-dir

Default: ~/.pkl/cache
Example: /path/to/module/cache/
The cache directory for storing packages.

--no-cache

Disable caching of packages.

-e, --env-var

Default: OS environment variables for the current process
Example: MY_VAR=myValue
Sets an environment variable that can be read by Pkl code with read("env:<envVarName>"). Repeat this option to set multiple environment variables.

-h, --help

Display help information.

--module-path

Default: (empty)
Example: dir1:zip1.zip:jar1.jar
Directories, ZIP archives, or JAR archives to search when resolving modulepath: URIs. Paths are separated by the platform-specific path separator (: on *nix, ; on Windows). Relative paths are resolved against the working directory.

-p, --property

Default: (none)
Example: myProp=myValue
Sets an external property that can be read by Pkl code with read("prop:<propertyName>"). Repeat this option to set multiple external properties.

--root-dir

Default: (none)
Example: /some/path
Root directory for file: modules and resources. If set, access to file-based modules and resources is restricted to those located under the specified root directory. Any symlinks are resolved before this check is performed.

--settings

Default: (none)
Example: mySettings.pkl
File path of the Pkl settings file to use. If not set, ~/.pkl/settings.pkl or defaults specified in the pkl.settings standard library module are used.

-t, --timeout

Default: (none)
Example: 30
Duration, in seconds, after which evaluation of a source module will be timed out. Note that a timeout is treated the same as a program error in that any subsequent source modules will not be evaluated.

-v, --version

Display version information.

-w, --working-dir

Base path that relative module paths passed as command-line arguments are resolved against. Defaults to the current working directory.

--ca-certificates

Default: (none)
Example: /some/path/certificates.pem
Path to a file containing CA certificates to be used for TLS connections.

Setting this option replaces the existing set of CA certificates bundled into the CLI. Certificates need to be X.509 certificates in PEM format.

For other methods of configuring certificates, see CA Certificates.

--http-proxy

Default: (none)
Example: http://proxy.example.com:1234
Configures HTTP connections to connect to the provided proxy address. The URI must have scheme http, and may not contain anything other than a host and port.

--http-no-proxy

Default: (none)
Example: example.com,169.254.0.0/16
Comma separated list of hosts to which all connections should bypass the proxy. Hosts can be specified by name, IP address, or IP range using CIDR notation.

The pkl eval, pkl test, pkl repl, pkl download-package, and pkl analyze imports commands also take the following options:

--project-dir

Default: (none)
Example: /some/path
Directory where the project lives.

A project is a directory that contains a PklProject file, which is used to declare package dependencies, as well as common evaluator settings to be applied in the project.

If omitted, this is determined by searching up from the working directory for a directory that contains a PklProject file, until --root-dir or the file system root is reached.

--omit-project-settings

Disables loading evaluator settings from the PklProject file.

--no-project

Disables all behavior related to projects.

Evaluating Modules

Say we have the following module:

config.pkl
bird {
  species = "Pigeon"
  diet = "Seeds"
}
parrot = (bird) {
  species = "Parrot"
  diet = "Berries"
}

To evaluate this module and write its output to standard output, run:

pkl eval config.pkl

You should see the following output:

bird {
  species = "Pigeon"
  diet = "Seeds"
}
parrot {
  species = "Parrot"
  diet = "Berries"
}

To render output as JSON, YAML, XML property list, or Java properties, use --format json, --format yaml, --format plist, or --format properties, respectively.

To control the output format from within Pkl code, see Module Output.

To read a source module from standard input rather than a file, use - as a module name:

echo mod2.pkl | pkl eval mod1.pkl - mod3.pkl

This is especially useful in environments that don’t support /dev/stdin.

To write output to a file rather than standard output, use --output-path some/file.ext.

Batch Evaluation

Multiple modules can be evaluated at once:

pkl eval config1.pkl config2.pkl config3.pkl

To write module outputs to separate output files, --output-path supports the following placeholders:

%{moduleDir}

the directory path of the source module, relative to the working directory (only available for file based modules)

%{moduleName}

the last path segment of the module URI, without file extension

%{outputFormat}

the target format (only available if --format is set)

The following run produces three JSON files placed next to the given source modules:

pkl eval --format=json --output-path=%{moduleDir}/%{moduleName}.json config1.pkl config2.pkl config3.pkl

If multiple module outputs are written to the same file, or to standard output, their outputs are concatenated. By default, module outputs are separated with ---, as in a YAML stream. The separator can be customized using the --module-output-separator option.

Working with the REPL

To start a REPL session, run pkl repl:

$ pkl repl
Welcome to Pkl 0.27.0.
Type an expression to have it evaluated.
Type :help or :examples for more information.

pkl>
The Java executable is named jpkl.

Loading Modules

To load config.pkl into the REPL, run:

pkl> :load config.pkl

To evaluate the bird.species property, run:

pkl> bird.species
"Pigeon"

To evaluate the entire module, force-evaluate this:

pkl> :force this

REPL Commands

Commands start with : and can be tab-completed:

pkl> :Tab
clear       examples    force       help        load        quit        reset
pkl> :qTab
pkl> :quitReturn
$

Commands can be abbreviated with any unique name prefix:

pkl> :qReturn
$

To learn more about each command, run the :help command.

Some commands support further command-specific tab completion. For example, the :load command supports completing file paths.

With commands out of the way, let’s move on to evaluating code.

Evaluating Code

To evaluate an expression, type the expression and hit Return.

pkl> 2 + 4
6

Apart from expressions, the REPL also accepts property, function, and class definitions. (See the Language Reference to learn more about these language concepts.)

pkl> hello = "Hello, World!"
pkl> hello
"Hello, World!"
pkl> function double(n) = 2 * n
pkl> double(5)
10
pkl> class Bird { species: String }
pkl> new Bird { species = "Pigeon" }
{
  name = ?
}

Top-level expressions are only supported in the REPL. In a regular module, every expression is contained in a definition, and only definitions exist at the top level.

Redefining Members

Existing members can be redefined:

pkl> species = "Pigeon"
pkl> species
"Pigeon"
pkl> species = "Barn"
pkl> species
"Barn"
pkl> species + " Owl"
pkl> species
"Barn Owl"

Due to Pkl’s late binding semantics, redefining a member affects dependent members:

pkl> name = "Barn"
pkl> species = "\(name) Owl"
pkl> species
"Barn Owl"
pkl> name = "Elf"
pkl> species
"Elf Owl"

Redefining members is only supported in the REPL. Under the hood, it works as follows:

  • The REPL environment is represented as a synthetic Pkl module.

  • When a new member is defined, it is added to the current REPL module.

  • When an existing member is redefined, it is added to a new REPL module that amends the previous REPL module.

Settings File

The Pkl settings file allows to customize the CLI experience.

A settings file is a Pkl module amending the pkl.settings standard library module. Its default location is ~/.pkl/settings.pkl. To use a different settings file, set the --settings command line option, for example --settings mysettings.pkl. To enforce default settings, use --settings pkl:settings. The settings file is also honored by (and configurable through) the Gradle plugin and CliEvaluator API.

Here is a typical settings file:

~/.pkl/settings.pkl
amends "pkl:settings" (1)

editor = Idea (2)
1 A settings file should amend the pkl.settings standard library module.
2 Configures IntelliJ IDEA as the preferred editor. Other supported values are System, GoLand, TextMate, Sublime, Atom, and VsCode.

With the above settings file in place, Cmd+Double-clicking a source code link in a stack trace opens the corresponding file in IntelliJ IDEA at the correct location.

To learn more about available settings, see pkl.settings.

CA Certificates

When making TLS requests, Pkl comes with its own set of CA certificates. These certificates can be overridden via either of the two options:

  • Set them directly via the CLI option --ca-certificates <path>.

  • Add them to a directory at path ~/.pkl/cacerts/.

Both these options will replace the default CA certificates bundled with Pkl.
The CLI option takes precedence over the certificates in ~/.pkl/cacerts/.
Certificates need to be X.509 certificates in PEM format.

HTTP Proxying

When making HTTP(s) requests, Pkl can possibly make use of an HTTP proxy.

There are two values that determine proxy settings; the proxy address and list of noproxy rules. When determining proxy settings, Pkl will look at the following locations, in order of precedence (lowest to highest):

  1. OS settings (on macOS, Windows, and GNOME environments)

  2. Settings file

  3. PklProject file

  4. --http-proxy and --http-no-proxy CLI flags

The proxy and noproxy values are individually set. For example, using the --http-no-proxy flag but not the --http-proxy flag will cause Pkl to look at the PklProject file, then the settings file, then system settings for the proxy address.

One exception to this rule is that setting a proxy address will cause Pkl to ignore any noproxy values set at the OS level.

Pkl only supports HTTP proxies, so neither HTTPS nor SOCKS proxies are supported. Pkl does not support authentication with a proxy.

When specifying a proxy address, it must have scheme http, and may not contain anything other than a host and port.

Proxy exclusions

Pkl can be configured to bypass the proxy for specific requests via a proxy exclusion rule (i.e. the --http-no-proxy flag). It may be provided either as a hostname, an IP address, or an IP range via CIDR notation. When determining whether a proxy should be excluded, hostnames do not get resolved to their IP address. For example, a request to localhost will not match --http-no-proxy=127.0.0.1.

For individual hosts (not CIDRs), ports can be specified. When no port is specified for a given host, connections to all ports bypass the proxy.

A hostname will match all of its subdomains. For example, example.com will match foo.example.com, and bar.foo.example.com, but not fooexample.com.

The value * is a special wildcard that matches all hosts.

A port can optionally be specified. If omitted, it matches all ports.

Pkl follows the rules described in Standardizing no_proxy, except it does not look at environment variables.