mirror of
https://github.com/apple/pkl.git
synced 2026-03-21 00:29:07 +01:00
Initial commit
This commit is contained in:
6
docs/antora.yml
Normal file
6
docs/antora.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
name: main
|
||||
title: Main Project
|
||||
version: 0.25.0-dev
|
||||
prerelease: true
|
||||
nav:
|
||||
- nav.adoc
|
||||
35
docs/docs.gradle.kts
Normal file
35
docs/docs.gradle.kts
Normal file
@@ -0,0 +1,35 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
|
||||
|
||||
plugins {
|
||||
pklAllProjects
|
||||
pklKotlinTest
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
test {
|
||||
java {
|
||||
srcDir(file("modules/pkl-core/examples"))
|
||||
srcDir(file("modules/pkl-config-java/examples"))
|
||||
}
|
||||
val kotlin = project.extensions
|
||||
.getByType<KotlinJvmProjectExtension>()
|
||||
.sourceSets[name]
|
||||
.kotlin
|
||||
kotlin.srcDir(file("modules/pkl-config-kotlin/examples"))
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(project(":pkl-core"))
|
||||
testImplementation(project(":pkl-config-java"))
|
||||
testImplementation(project(":pkl-config-kotlin"))
|
||||
testImplementation(project(":pkl-commons-test"))
|
||||
testImplementation(libs.junitEngine)
|
||||
testImplementation(libs.antlrRuntime)
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
inputs.files(fileTree("modules").matching {
|
||||
include("**/pages/*.adoc")
|
||||
})
|
||||
}
|
||||
36
docs/gradle.lockfile
Normal file
36
docs/gradle.lockfile
Normal file
@@ -0,0 +1,36 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.tunnelvisionlabs:antlr4-runtime:4.9.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
io.leangen.geantyref:geantyref:1.3.14=testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.12.21=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata
|
||||
org.assertj:assertj-core:3.24.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.graalvm.sdk:graal-sdk:22.3.1=testRuntimeClasspath
|
||||
org.graalvm.truffle:truffle-api:22.3.1=testRuntimeClasspath
|
||||
org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:1.7.10=kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-script-runtime:1.7.10=kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath
|
||||
org.jetbrains.kotlin:kotlin-scripting-common:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
|
||||
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
|
||||
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
|
||||
org.jetbrains.kotlin:kotlin-scripting-jvm:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
|
||||
org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10=kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.10=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib:1.7.10=kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.jetbrains:annotations:13.0=kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-api:5.9.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
|
||||
org.junit.jupiter:junit-jupiter-engine:5.9.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
|
||||
org.junit.jupiter:junit-jupiter-params:5.9.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-commons:1.9.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
|
||||
org.junit.platform:junit-platform-engine:1.9.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
|
||||
org.junit:junit-bom:5.9.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
|
||||
org.opentest4j:opentest4j:1.2.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
|
||||
org.organicdesign:Paguro:3.10.3=testRuntimeClasspath
|
||||
org.snakeyaml:snakeyaml-engine:2.5=testRuntimeClasspath
|
||||
empty=annotationProcessor,apiDependenciesMetadata,archives,compile,compileClasspath,compileOnly,compileOnlyDependenciesMetadata,default,implementationDependenciesMetadata,intransitiveDependenciesMetadata,kotlinCompilerPluginClasspath,kotlinNativeCompilerPluginClasspath,kotlinScriptDef,kotlinScriptDefExtensions,runtime,runtimeClasspath,runtimeOnlyDependenciesMetadata,testAnnotationProcessor,testApiDependenciesMetadata,testCompile,testCompileOnly,testCompileOnlyDependenciesMetadata,testIntransitiveDependenciesMetadata,testKotlinScriptDef,testKotlinScriptDefExtensions,testRuntime
|
||||
8
docs/modules/ROOT/pages/community.adoc
Normal file
8
docs/modules/ROOT/pages/community.adoc
Normal file
@@ -0,0 +1,8 @@
|
||||
= Community
|
||||
:uri-github-issue: https://github.com/apple/pkl/issues
|
||||
:uri-github-discussions: https://github.com/apple/pkl/discussions
|
||||
|
||||
We'd love to hear from you!
|
||||
|
||||
* Create an {uri-github-issue}[issue]
|
||||
* Ask questions on {uri-github-discussions}[GitHub Discussions]
|
||||
14
docs/modules/ROOT/pages/examples.adoc
Normal file
14
docs/modules/ROOT/pages/examples.adoc
Normal file
@@ -0,0 +1,14 @@
|
||||
= Examples
|
||||
|
||||
:uri-github-apple: https://github.com/apple
|
||||
:uri-jvm-examples: {uri-github-apple}/pkl-jvm-examples
|
||||
:uri-go-examples: {uri-github-apple}/pkl-go-examples
|
||||
:uri-swift-examples: {uri-github-apple}/pkl-swift-examples
|
||||
:uri-k8s-examples: {uri-github-apple}/pkl-k8s-examples
|
||||
|
||||
For ready-to-go examples with full source code, see the various repositories that are available for you.
|
||||
|
||||
* {uri-jvm-examples}[pkl-jvm-examples] -- for using Pkl within the JVM
|
||||
* {uri-swift-examples}[pkl-swift-examples] -- for using Pkl with Swift
|
||||
* {uri-go-examples}[pkl-go-examples] -- for using Pkl with Go
|
||||
* {uri-k8s-examples}[pkl-k8s-examples] -- for using Pkl with Kubernetes
|
||||
15
docs/modules/ROOT/pages/index.adoc
Normal file
15
docs/modules/ROOT/pages/index.adoc
Normal file
@@ -0,0 +1,15 @@
|
||||
= User Manual
|
||||
include::../partials/component-attributes.adoc[]
|
||||
|
||||
Quick Links: xref:pkl-cli:index.adoc#installation[Installation] | xref:language-reference:index.adoc[Language Reference]
|
||||
|
||||
Pkl -- pronounced _Pickle_ -- is an embeddable configuration language which provides rich support for data templating and validation.
|
||||
It can be used from the command line, integrated in a build pipeline, or embedded in a program.
|
||||
Pkl scales from small to large, simple to complex, ad-hoc to repetitive configuration tasks.
|
||||
|
||||
xref:introduction:index.adoc[Introduction]:: Why we created Pkl and what it can do for you.
|
||||
xref:language.adoc[Language]:: Get to know the language and standard library.
|
||||
xref:language-bindings.adoc[Bindings]:: Libraries for embedding Pkl into general-purpose languages.
|
||||
xref:tools.adoc[Tools]:: CLI, Gradle plugin, code generators, and other tools.
|
||||
link:{uri-pkl-examples-repo}[Examples]:: Ready-to-go examples with full source code.
|
||||
xref:release-notes:index.adoc[Release Notes]:: What's new in each release.
|
||||
3
docs/modules/ROOT/pages/integrations.adoc
Normal file
3
docs/modules/ROOT/pages/integrations.adoc
Normal file
@@ -0,0 +1,3 @@
|
||||
= Framework Integrations
|
||||
|
||||
* xref:spring:ROOT:index.adoc[Spring (Boot) Integration]
|
||||
6
docs/modules/ROOT/pages/language-bindings.adoc
Normal file
6
docs/modules/ROOT/pages/language-bindings.adoc
Normal file
@@ -0,0 +1,6 @@
|
||||
= Language Bindings
|
||||
|
||||
* xref:java-binding:index.adoc[Java]
|
||||
* xref:kotlin-binding:index.adoc[Kotlin]
|
||||
* xref:swift:ROOT:index.adoc[Swift]
|
||||
* xref:go:ROOT:index.adoc[Go]
|
||||
5
docs/modules/ROOT/pages/language.adoc
Normal file
5
docs/modules/ROOT/pages/language.adoc
Normal file
@@ -0,0 +1,5 @@
|
||||
= Language
|
||||
|
||||
* xref:language-tutorial:index.adoc[Tutorial]
|
||||
* xref:language-reference:index.adoc[Language Reference]
|
||||
* xref:standard-library.adoc[Standard Library]
|
||||
7
docs/modules/ROOT/pages/resources.adoc
Normal file
7
docs/modules/ROOT/pages/resources.adoc
Normal file
@@ -0,0 +1,7 @@
|
||||
= Resources
|
||||
include::../partials/component-attributes.adoc[]
|
||||
|
||||
There's more to explore!
|
||||
|
||||
* Visit Pkl's repositories on https://github.com/apple?q=pkl[GitHub]
|
||||
* Browse the standard library's https://pkl-lang.org/package-docs/pkl/{pkl-version}/[API Docs]
|
||||
13
docs/modules/ROOT/pages/standard-library.adoc
Normal file
13
docs/modules/ROOT/pages/standard-library.adoc
Normal file
@@ -0,0 +1,13 @@
|
||||
= Standard Library
|
||||
include::../partials/component-attributes.adoc[]
|
||||
|
||||
The standard library is a set of Pkl modules, versioned and distributed together with the language.
|
||||
It is documented in the link:{uri-pkl-stdlib-docs-index}[API Docs].
|
||||
|
||||
To import a standard library module, use `import "pkl:<identifier>"`.
|
||||
For example, `import "pkl:json"` imports the `pkl.json` module.
|
||||
|
||||
The `pkl.base` module defines the most fundamental properties, methods, and classes for using Pkl.
|
||||
Its members are automatically available in every module and hence, it does not need to be imported.
|
||||
|
||||
The default module allowlist (`--allowed-modules`) grants access to all standard library modules.
|
||||
10
docs/modules/ROOT/pages/tools.adoc
Normal file
10
docs/modules/ROOT/pages/tools.adoc
Normal file
@@ -0,0 +1,10 @@
|
||||
= Tools
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
|
||||
* xref:pkl-cli:index.adoc[CLI]
|
||||
* xref:pkl-doc:index.adoc[Pkldoc]
|
||||
* xref:pkl-gradle:index.adoc[Gradle Plugin]
|
||||
* Editor support
|
||||
** xref:intellij:ROOT:index.adoc[IntelliJ]
|
||||
** xref:vscode:ROOT:index.adoc[VSCode]
|
||||
** xref:neovim:ROOT:index.adoc[Neovim]
|
||||
64
docs/modules/ROOT/partials/component-attributes.adoc
Normal file
64
docs/modules/ROOT/partials/component-attributes.adoc
Normal file
@@ -0,0 +1,64 @@
|
||||
// TODO: move to antora.yml once supported
|
||||
|
||||
// the following attributes must be updated immediately before a release
|
||||
|
||||
// pkl version corresponding to current git commit without -dev suffix or git hash
|
||||
:pkl-version-no-suffix: 0.25.0
|
||||
// tells whether pkl version corresponding to current git commit
|
||||
// is a release version (:is-release-version: '') or dev version (:!is-release-version:)
|
||||
:!is-release-version:
|
||||
|
||||
// the remaining attributes do not need to be updated regularly
|
||||
|
||||
:pkl-version: {pkl-version-no-suffix}-dev
|
||||
ifdef::is-release-version[]
|
||||
:pkl-version: {pkl-version-no-suffix}
|
||||
endif::[]
|
||||
|
||||
// use non-unique snapshot version because we have no way to determine unique snapshot version here
|
||||
:pkl-artifact-version: {pkl-version-no-suffix}-SNAPSHOT
|
||||
ifdef::is-release-version[]
|
||||
:pkl-artifact-version: {pkl-version}
|
||||
endif::[]
|
||||
|
||||
:uri-maven-docsite: https://central.sonatype.com/
|
||||
|
||||
:uri-sonatype: https://s01.oss.sonatype.org/service/local/repositories/snapshots/content/
|
||||
|
||||
:symbolic-version-name: latest
|
||||
ifdef::is-release-version[]
|
||||
:symbolic-version-name: current
|
||||
endif::[]
|
||||
:uri-pkl-docs-base: https://pkl-lang.org/package-docs
|
||||
:uri-pkl-stdlib-docs-base: {uri-pkl-docs-base}/pkl
|
||||
:uri-pkl-stdlib-docs: {uri-pkl-stdlib-docs-base}/{pkl-version}
|
||||
:uri-pkl-stdlib-docs-index: {uri-pkl-stdlib-docs}/
|
||||
|
||||
// TODO(oss): check these links when we have tags
|
||||
:github-branch: main
|
||||
ifdef::is-release-version[]
|
||||
:github-branch: v{pkl-version-no-suffix}
|
||||
endif::[]
|
||||
:uri-github-tree: https://github.com/apple/pkl/tree/{github-branch}
|
||||
:uri-pkl-stdlib-sources: {uri-github-tree}/stdlib
|
||||
|
||||
:github-releases-base: https://github.com/apple/pkl/releases
|
||||
:github-releases: {github-releases-base}/download/{pkl-artifact-version}
|
||||
|
||||
:uri-pkl-core-main-sources: {uri-github-tree}/pkl-core/src/main/java/org/pkl/core
|
||||
:uri-pkl-cli-main-sources: {uri-github-tree}/pkl-cli/src/main/kotlin/org/pkl/cli
|
||||
:uri-pkl-doc-main-sources: {uri-github-tree}/pkl-doc/src/main/kotlin/org/pkl/doc
|
||||
|
||||
// This attribute is used as language for Pkl code blocks.
|
||||
// It can then be mapped to different languages in different environments (e.g., IntelliJ vs. Antora).
|
||||
:pkl: pkl
|
||||
:pkl-expr: pkl expression
|
||||
|
||||
:uri-pkl-examples-repo: https://github.com/apple/pkl-jvm-examples
|
||||
:uri-pkl-examples-tree: {uri-pkl-examples-repo}/tree/main
|
||||
:uri-build-eval-example: {uri-pkl-examples-tree}/build-eval
|
||||
:uri-codegen-java-example: {uri-pkl-examples-tree}/codegen-java
|
||||
:uri-codegen-kotlin-example: {uri-pkl-examples-tree}/codegen-kotlin
|
||||
:uri-config-java-example: {uri-pkl-examples-tree}/config-java
|
||||
:uri-config-kotlin-example: {uri-pkl-examples-tree}/config-kotlin
|
||||
:uri-pkldoc-example: {uri-pkl-examples-tree}/pkldoc
|
||||
93
docs/modules/introduction/pages/comparison.adoc
Normal file
93
docs/modules/introduction/pages/comparison.adoc
Normal file
@@ -0,0 +1,93 @@
|
||||
= Comparison
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
:uri-jsonnet: https://jsonnet.org
|
||||
:uri-hcl: https://github.com/hashicorp/hcl
|
||||
:uri-dhall: https://dhall-lang.org
|
||||
:uri-pkl-spring: https://github.com/apple/pkl-spring
|
||||
:uri-graalvm: https://www.graalvm.org
|
||||
|
||||
Configuration is often described in a static configuration format or is generated with a general-purpose programming language.
|
||||
This page lists shortcomings of these approaches and explains how Pkl addresses them.
|
||||
Also, Pkl's strong and weak points in comparison to other configuration languages are discussed in this document.
|
||||
|
||||
[[static-config-formats]]
|
||||
== Pkl vs. Static Config Formats
|
||||
|
||||
Static configuration formats such as JSON, YAML, and XML work reasonably well for simple configuration needs.
|
||||
However, they do have some shortcomings, including:
|
||||
|
||||
. They are not very human-friendly to read and write. (JSON, XML)
|
||||
. They do not provide a way to split a large file into multiple smaller ones. (JSON, YAML)
|
||||
. They offer no way or very limited ways to abstract over repetitive configuration. (JSON, YAML, XML)
|
||||
. They do not offer standardized or widely available schema validators. (JSON, YAML)
|
||||
. They offer little or no schema-aware tooling. (JSON, YAML)
|
||||
|
||||
Pkl addresses these shortcomings as follows:
|
||||
|
||||
. It has a clutter-free and familiar syntax with nestable comments.
|
||||
. Modules can import other modules from local and remote locations.
|
||||
. Every object can act as a template for other objects.
|
||||
The standard library offers strong support for data manipulation.
|
||||
. It has strong built-in support for describing and validating configuration schemas.
|
||||
. It is designed to enable schema-aware tooling, such as REPLs and editors with code completion support.
|
||||
|
||||
[[general-purpose-langs]]
|
||||
== Pkl vs. General-purpose Languages
|
||||
|
||||
When configuration needs outgrow the capabilities of static configuration formats,
|
||||
projects often turn to generate configuration with a general-purpose programming language such as Python.
|
||||
Given enough effort, this approach can satisfy complex configuration needs.
|
||||
However, expressing configuration in a full-blown programming language does have some shortcomings, including:
|
||||
|
||||
. Reading, writing, and debugging configuration can become as challenging as reading, writing, and debugging application code.
|
||||
. The host language may not be a good fit for describing, manipulating, and abstracting over hierarchical configuration.
|
||||
. Configuration code may not visually resemble the configuration it generates.
|
||||
. The host language may not be a good fit for defining and validating configuration schemas.
|
||||
. Development environments may offer little help for developing and validating configuration written in the host language.
|
||||
. General-purpose languages are powerful and often difficult to sandbox.
|
||||
Are you certain your configuration script isn't erasing your hard disk or launching a rocket?
|
||||
|
||||
Pkl addresses these shortcomings as follows:
|
||||
|
||||
. As an expression-oriented and side-effect free language, it eliminates many potential sources of errors.
|
||||
. It is specifically designed for describing, manipulating, and abstracting over hierarchical configuration.
|
||||
. Pkl code often resembles the configuration it generates.
|
||||
. It has strong built-in support for defining and validating configuration schemas.
|
||||
. It is designed to enable advanced and schema-aware tooling.
|
||||
. It is comparatively powerless and strictly sandboxed, making fatal configuration mistakes and exploits less likely.
|
||||
Till now, we haven't spotted any Pkl script capable of erasing your hard disk.
|
||||
|
||||
[[other-config-langs]]
|
||||
== Pkl vs. Other Config Languages
|
||||
|
||||
Compared to open-source configuration languages such as link:{uri-jsonnet}[Jsonnet],
|
||||
link:{uri-hcl}[HCL], and link:{uri-dhall}[Dhall], Pkl's strong points are:
|
||||
|
||||
General::
|
||||
+
|
||||
* Pkl has a clean and familiar syntax, which makes it easier to read and learn.
|
||||
* Pkl supports writing sophisticated schemas, which enables config validation, code and documentation generation, and advanced IDE support.
|
||||
This is Pkl's most significant differentiator, and is the main reason why we created it.
|
||||
* Pkl has stronger templating capabilities than other config languages, reducing user code to the absolute minimum.
|
||||
|
||||
Embedding::
|
||||
+
|
||||
* Pkl is great for embedding into JVM applications.
|
||||
* Pkl offers modern xref:java-binding:pkl-config-java.adoc[JVM libraries] for runtime application configuration.
|
||||
* Pkl supports xref:java-binding:codegen.adoc[code generation] to enable statically typed access to configuration from programming languages.
|
||||
* Pkl integrates with third-party (link:{uri-pkl-spring}[Spring Boot]) JVM libraries and frameworks.
|
||||
|
||||
Tooling::
|
||||
+
|
||||
* Pkl has a polished xref:pkl-doc:index.adoc[documentation generator] that produces highly navigable and searchable documentation.
|
||||
* Pkl offers a xref:pkl-gradle:index.adoc[Gradle plugin] to easily integrate code evaluation, documentation generation, and code generation into your builds.
|
||||
* Pkl's native executables have a link:{uri-graalvm}[JIT compiler] that can speed up evaluation up to hundred times.
|
||||
|
||||
On the other hand, we believe that Pkl's weak points are:
|
||||
|
||||
* Pkl's native binaries are larger than those of other config languages.
|
||||
* Pkl is less known and has a smaller community than some other config languages.
|
||||
|
||||
We are working towards making Pkl overcome these weakness. Please support us in reaching this goal!
|
||||
|
||||
We hope that you will enjoy Pkl, and that you trust us to gradually improve its weak points.
|
||||
97
docs/modules/introduction/pages/concepts.adoc
Normal file
97
docs/modules/introduction/pages/concepts.adoc
Normal file
@@ -0,0 +1,97 @@
|
||||
= Concepts
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
:uri-property-list: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/PropertyLists/UnderstandXMLPlist/UnderstandXMLPlist.html
|
||||
|
||||
Let's get to know Pkl by discussing some of its concepts and features.
|
||||
|
||||
[[abstraction]]
|
||||
== Abstraction
|
||||
|
||||
Configuration tends to grow larger and more complex over time, making it increasingly difficult to understand and maintain.
|
||||
Pkl can reduce the size and complexity of configuration by
|
||||
|
||||
* describing similar configuration elements in terms of their differences
|
||||
* introducing abstractions for common configuration elements
|
||||
* separating configuration structure from configuration data
|
||||
* computing instead of enumerating configuration
|
||||
|
||||
[[evaluation]]
|
||||
== Evaluation
|
||||
|
||||
Pkl code lives in _modules_, a more fancy and general term for _files_.
|
||||
Evaluating a module produces an in-memory _data model_ that is roughly comparable to a JSON data model.
|
||||
If evaluation completes successfully, the Pkl evaluator converts the data model to an external representation and terminates with the status code zero.
|
||||
Otherwise, the evaluator prints an error message and terminates with a non-zero status code.
|
||||
|
||||
[[immutability]]
|
||||
== Immutability
|
||||
|
||||
All Pkl data is immutable.
|
||||
Manipulating a value always returns a new value, leaving the original value unchanged.
|
||||
Immutability eliminates many potential sources of errors.
|
||||
|
||||
[[isolation]]
|
||||
== Isolation
|
||||
|
||||
Evaluation of Pkl code is strictly sandboxed.
|
||||
Except for a few well-defined and well-controlled exceptions, Pkl code cannot interact with the outside world.
|
||||
Leaving aside bugs in the language implementation, the worst thing that buggy or malicious Pkl code can do is to consume CPU and memory resources until the evaluator gets killed.
|
||||
Over time, sandboxing will be further strengthened to cover fine-grained CPU and memory boxing.
|
||||
|
||||
[[rendering]]
|
||||
== Rendering
|
||||
|
||||
Converting a data model to an external representation is called _rendering_ the model.
|
||||
Pkl ships with renderers for the following data formats:
|
||||
|
||||
* JSON
|
||||
* Jsonnet
|
||||
* Pcf (a static subset of Pkl)
|
||||
* (Java) Properties
|
||||
* {uri-property-list}[Property List]
|
||||
* XML
|
||||
* YAML
|
||||
|
||||
Support for other formats can be added by writing a custom renderer in Pkl or Java.
|
||||
See xref:language-reference:index.adoc#module-output[Module Output] and xref:pkl-core:index.adoc#value-visitor[Value Visitor] for more information.
|
||||
|
||||
[[resemblance]]
|
||||
== Resemblance
|
||||
|
||||
By design, Pkl code tends to structurally and visually resemble the configuration it generates.
|
||||
This makes the code easier to read and write.
|
||||
|
||||
[[reuse]]
|
||||
== Reuse
|
||||
|
||||
Modules can reuse other modules by xref:language-reference:index.adoc#import-module[importing] them from local or remote locations.
|
||||
Imports can also be used to split up one large module into multiple smaller ones, increasing maintainability.
|
||||
A configurable security policy helps to keep imports under control.
|
||||
|
||||
[[schema]]
|
||||
== Schema
|
||||
|
||||
Configuration is structured data.
|
||||
Pkl supports -- but does not require -- to express this structure as a _configuration schema_, a set of classes defining configuration properties, their defaults, types, and constraints.
|
||||
Writing and maintaining a configuration schema takes some effort but, in return, provides these benefits:
|
||||
|
||||
* Independent evolution of configuration schema and configuration data, often by different teams (for example service providers and service consumers).
|
||||
* Automatic xref:pkl-doc:index.adoc[documentation generation].
|
||||
* Strong validation of configuration, both during development time and runtime.
|
||||
* Statically typed access to configuration from xref:java-binding:codegen.adoc[Java] and other languages through code generation.
|
||||
* Schema-aware development tools, for example REPLs and editors with code completion support.
|
||||
|
||||
[[template]]
|
||||
== Templating
|
||||
|
||||
Pkl supports writing templates for objects and entire modules.
|
||||
Templates can be repeatedly turned into concrete configuration by filling in the blanks, and -- when necessary -- overriding defaults.
|
||||
Sharing template modules over the network can streamline complex configuration tasks for entire teams, organizations, and communities.
|
||||
|
||||
[[usability]]
|
||||
== Usability
|
||||
|
||||
Everybody needs a configuration solution, but nobody wants to spend a lot of time learning it.
|
||||
To reflect this reality, Pkl has a strong focus on usability.
|
||||
For example, error messages explain causes and possible solutions and object properties maintain definition order to avoid surprises.
|
||||
We hope that this focus on usability will make Pkl accessible to a wide audience of occasional users, while still leaving room for expert users and advanced use cases.
|
||||
13
docs/modules/introduction/pages/index.adoc
Normal file
13
docs/modules/introduction/pages/index.adoc
Normal file
@@ -0,0 +1,13 @@
|
||||
= Introduction
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
|
||||
Pkl -- pronounced _Pickle_ -- is a configuration-as-code language with rich validation and tooling.
|
||||
It can be used as a command line tool, software library, or build plugin.
|
||||
Pkl scales from small to large, simple to complex, ad-hoc to recurring configuration tasks.
|
||||
|
||||
We created Pkl because we believe that configuration is best expressed in a special-purpose configuration language;
|
||||
a blend between a static configuration format, and a general-purpose programming language.
|
||||
|
||||
* xref:use-cases.adoc[Use Cases]
|
||||
* xref:concepts.adoc[Concepts]
|
||||
* xref:comparison.adoc[Comparison]
|
||||
35
docs/modules/introduction/pages/use-cases.adoc
Normal file
35
docs/modules/introduction/pages/use-cases.adoc
Normal file
@@ -0,0 +1,35 @@
|
||||
= Use Cases
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
:uri-kotlin-homepage: https://kotlinlang.org
|
||||
:uri-xml-property-lists: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/PropertyLists/UnderstandXMLPlist/UnderstandXMLPlist.html
|
||||
|
||||
Pkl is a good fit for:
|
||||
|
||||
Generating Static Configuration::
|
||||
Are you using a tool, service, or application that is configured with JSON, YAML, or any other static configuration format?
|
||||
+
|
||||
By generating this configuration with Pkl, you can reduce verbosity and increase maintainability through xref:concepts.adoc#reuse[reuse], xref:concepts.adoc#template[templating], and xref:concepts.adoc#abstraction[abstraction].
|
||||
JSON, YAML, and {uri-xml-property-lists}[XML property lists] are supported out of the box; xref:concepts.adoc#rendering[renderers] for other configuration formats can be developed and shared by anyone.
|
||||
Automatic defaults, strong validation, and sensible error messages come in reach with configuration xref:concepts.adoc#schema[schemas].
|
||||
+
|
||||
Generation can be triggered manually, by an automation pipeline, or by the target application.
|
||||
|
||||
Application Runtime Configuration::
|
||||
Are you the author of a tool, service, or application that consumes configuration?
|
||||
+
|
||||
By adopting Pkl as your "native" configuration solution (rather than, say, using it to generate JSON files), you benefit from a modern xref:java-binding:pkl-config-java.adoc[configuration library] that is safe, easy, and enjoyable to use.
|
||||
At the same time, anyone configuring your application -- whether that's your users, site reliability engineers (SREs), or yourself -- benefit from a well-defined, well-documented, and scalable configuration language.
|
||||
+
|
||||
At the time of writing, Pkl offers configuration libraries for the JVM runtime, Swift, and also for Golang.
|
||||
+
|
||||
We maintian the following libraries:
|
||||
+
|
||||
* xref:java-binding:pkl-config-java.adoc[pkl-config-java] for Java compatible languages
|
||||
* xref:kotlin-binding:pkl-config-kotlin.adoc[pkl-config-kotlin] for the {uri-kotlin-homepage}[Kotlin] language.
|
||||
* xref:swift:ROOT:index.adoc[pkl-swift] for the Swift language.
|
||||
* xref:go:ROOT:index.adoc[pkl-go] for the Go language.
|
||||
|
||||
In the future, we hope to add support for other popular languages and platforms, realizing our vision of a polyglot config solution based on a single config language.
|
||||
|
||||
<Your Use Case Here>::
|
||||
We are just getting started. Tell us about _your_ Pkl success story!
|
||||
22
docs/modules/java-binding/examples/JavaConfigExample.java
Normal file
22
docs/modules/java-binding/examples/JavaConfigExample.java
Normal file
@@ -0,0 +1,22 @@
|
||||
import org.pkl.config.java.Config;
|
||||
import org.pkl.config.java.ConfigEvaluator;
|
||||
import org.pkl.config.java.JavaType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
// the pkl/pkl-examples repo has a similar example
|
||||
public class JavaConfigExample {
|
||||
@Test
|
||||
public void usage() {
|
||||
// tag::usage[]
|
||||
Config config;
|
||||
try (var evaluator = ConfigEvaluator.preconfigured()) { // <1>
|
||||
config = evaluator.evaluateText(
|
||||
"pigeon { age = 5; diet = \"Seeds\" }"); // <2>
|
||||
}
|
||||
var pigeon = config.get("pigeon"); // <3>
|
||||
var age = pigeon.get("age").as(int.class); // <4>
|
||||
var diet = pigeon.get("diet").as(JavaType.listOf(String.class)); // <5>
|
||||
// end::usage[]
|
||||
}
|
||||
}
|
||||
180
docs/modules/java-binding/pages/codegen.adoc
Normal file
180
docs/modules/java-binding/pages/codegen.adoc
Normal file
@@ -0,0 +1,180 @@
|
||||
= Java Code Generator
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
:uri-pkl-codgen-java-maven-module: {uri-maven-docsite}/artifact/org.pkl-lang/pkl-codegen-java
|
||||
|
||||
The Java source code generator takes Pkl class definitions as an input, and generates corresponding Java classes with equally named properties.
|
||||
|
||||
The benefits of code generation are:
|
||||
|
||||
* Configuration can be conveniently consumed as statically typed Java objects.
|
||||
* The entire configuration tree can be code-completed in Java IDEs.
|
||||
* Any drift between Java code and Pkl configuration structure is caught at compile time.
|
||||
|
||||
The generated classes are immutable and have component-wise implementations of `equals()`, `hashCode()`, and `toString()`.
|
||||
|
||||
== Installation
|
||||
|
||||
The code generator is offered as Gradle plugin, Java library, and CLI.
|
||||
|
||||
=== Gradle Plugin
|
||||
|
||||
See xref:pkl-gradle:index.adoc#installation[Installation] in the Gradle plugin chapter.
|
||||
|
||||
[[install-library]]
|
||||
=== Java Library
|
||||
|
||||
The `pkl-codegen-java` library is available {uri-pkl-codgen-java-maven-module}[from Maven Central].
|
||||
It requires Java 11 or higher.
|
||||
|
||||
ifndef::is-release-version[]
|
||||
NOTE: Snapshots are published to repository `{uri-sonatype}`.
|
||||
endif::[]
|
||||
|
||||
==== Gradle
|
||||
|
||||
To use the library in a Gradle project, declare the following dependency:
|
||||
|
||||
[tabs]
|
||||
====
|
||||
Groovy::
|
||||
+
|
||||
.build.gradle
|
||||
[source,groovy,subs="+attributes"]
|
||||
----
|
||||
dependencies {
|
||||
compile "org.pkl-lang:pkl-codegen-java:{pkl-artifact-version}"
|
||||
}
|
||||
|
||||
ifndef::is-release-build[]
|
||||
repositories {
|
||||
maven { url "{uri-sonatype}" }
|
||||
}
|
||||
endif::[]
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
.build.gradle.kts
|
||||
[source,kotlin,subs="+attributes"]
|
||||
----
|
||||
dependencies {
|
||||
compile("org.pkl-lang:pkl-codegen-java:{pkl-artifact-version}")
|
||||
}
|
||||
|
||||
ifndef::is-release-build[]
|
||||
repositories {
|
||||
maven { url = uri("{uri-sonatype}") }
|
||||
}
|
||||
endif::[]
|
||||
----
|
||||
====
|
||||
|
||||
==== Maven
|
||||
|
||||
To use the library in a Maven project, declare the following dependency:
|
||||
|
||||
.pom.xml
|
||||
[source,xml,subs="+attributes"]
|
||||
----
|
||||
<project>
|
||||
<dependency>
|
||||
<groupId>org.pkl-lang</groupId>
|
||||
<artifactId>pkl-codegen-java</artifactId>
|
||||
<version>{pkl-artifact-version}</version>
|
||||
</dependency>
|
||||
ifndef::is-release-build[]
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>sonatype-s01</id>
|
||||
<name>Sonatype S01</name>
|
||||
<url>{uri-sonatype}</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
endif::[]
|
||||
</project>
|
||||
----
|
||||
|
||||
[[install-cli]]
|
||||
=== CLI
|
||||
|
||||
The CLI is bundled with the Java library.
|
||||
As we do not currently ship the CLI as a self-contained Jar, we recommend to provision it with a Maven compatible build tool as shown in <<install-library>>.
|
||||
|
||||
[[codegen-java-usage]]
|
||||
== Usage
|
||||
|
||||
The code generator is offered as Gradle plugin, Java library, and CLI.
|
||||
|
||||
=== Gradle Plugin
|
||||
|
||||
See xref:pkl-gradle:index.adoc#java-code-gen[Java Code Generation] in the Gradle plugin chapter.
|
||||
|
||||
=== Java Library
|
||||
|
||||
The Java library offers two APIs: a high-level API that corresponds to the CLI, and a lower-level API that provides additional features and control.
|
||||
The entry points for these APIs are `org.pkl.codegen.java.CliJavaCodeGenerator` and `org.pkl.codegen.java.JavaCodeGenerator`, respectively.
|
||||
For more information, refer to the Javadoc documentation.
|
||||
|
||||
=== CLI
|
||||
|
||||
As explained in <<install-cli,Installation>>, the CLI is bundled with the Java library.
|
||||
To run the CLI, execute the library Jar or its `org.pkl.codegen.java.Main` main class.
|
||||
|
||||
*Synopsis:* `java -cp <classpath> -jar pkl-codegen-java.jar [<options>] <modules>`
|
||||
|
||||
`<modules>`::
|
||||
The absolute or relative URIs of the modules to generate classes for.
|
||||
Relative URIs are resolved against the working directory.
|
||||
|
||||
==== Options
|
||||
|
||||
.--generate-getters
|
||||
[%collapsible]
|
||||
====
|
||||
Default: (flag not set) +
|
||||
Flag that indicates to generate private final fields and public getter methods instead of public final fields.
|
||||
====
|
||||
|
||||
.--generate-javadoc
|
||||
[%collapsible]
|
||||
====
|
||||
Default: (flag not set) +
|
||||
Flag that indicates to generate Javadoc based on doc comments for Pkl modules, classes, and properties.
|
||||
====
|
||||
|
||||
.--params-annotation
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `org.pkl.config.java.mapper.Named` +
|
||||
Fully qualified name of the annotation to use on constructor parameters.
|
||||
====
|
||||
|
||||
.--non-null-annotation
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `org.pkl.config.java.mapper.NonNull` +
|
||||
Fully qualified named of the annotation class to use for non-null types. +
|
||||
This annotation is required to have `java.lang.annotation.ElementType.TYPE_USE` as a `@Target`
|
||||
or it may generate code that does not compile.
|
||||
====
|
||||
|
||||
.--implement-serializable
|
||||
[%collapsible]
|
||||
====
|
||||
Default: (flag not set) +
|
||||
Whether to make generated classes implement `java.io.Serializable`.
|
||||
====
|
||||
|
||||
Common code generator options:
|
||||
|
||||
include::{partialsdir}/cli-codegen-options.adoc[]
|
||||
|
||||
Common CLI options:
|
||||
|
||||
include::../../pkl-cli/partials/cli-common-options.adoc[]
|
||||
|
||||
[[full-example]]
|
||||
== Full Example
|
||||
|
||||
For a ready-to-go example with full source code,
|
||||
see link:{uri-codegen-java-example}[codegen-java] in the _pkl/pkl-examples_ repository.
|
||||
4
docs/modules/java-binding/pages/index.adoc
Normal file
4
docs/modules/java-binding/pages/index.adoc
Normal file
@@ -0,0 +1,4 @@
|
||||
= Integration with Java
|
||||
|
||||
Pkl provides rich integration with Java. Our integration allows you to embed the Pkl runtime into your Java program, and also provides code generation from Pkl source files.
|
||||
|
||||
212
docs/modules/java-binding/pages/pkl-config-java.adoc
Normal file
212
docs/modules/java-binding/pages/pkl-config-java.adoc
Normal file
@@ -0,0 +1,212 @@
|
||||
= pkl-config-java Library
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
:uri-pkl-core-EvalException: {uri-pkl-core-main-sources}/EvalException.java
|
||||
|
||||
:uri-pkl-config-java-maven-module: {uri-maven-docsite}/artifact/org.pkl-lang/pkl-config-java-all
|
||||
:uri-pkl-config-java-main-sources: {uri-github-tree}/pkl-config-java/src/main/java/org/pkl/config/java
|
||||
:uri-pkl-config-java-test-sources: {uri-github-tree}/pkl-config-java/src/test/java/org/pkl/config/java
|
||||
:uri-pkl-config-java-test-resources: {uri-github-tree}/pkl-config-java/src/test/resources/org/pkl/config/java
|
||||
:uri-pkl-config-java-ConfigEvaluator: {uri-pkl-config-java-main-sources}/ConfigEvaluator.java
|
||||
:uri-pkl-config-java-Config: {uri-pkl-config-java-main-sources}/Config.java
|
||||
:uri-pkl-config-java-ValueMapper: {uri-pkl-config-java-main-sources}/mapper/ValueMapper.java
|
||||
:uri-pkl-config-java-Named: {uri-pkl-config-java-main-sources}/mapper/Named.java
|
||||
:uri-pkl-config-java-Conversion: {uri-pkl-config-java-main-sources}/mapper/Conversion.java
|
||||
:uri-pkl-config-java-Conversions: {uri-pkl-config-java-main-sources}/mapper/Conversions.java
|
||||
:uri-pkl-config-java-ConverterFactories: {uri-pkl-config-java-main-sources}/mapper/ConverterFactories.java
|
||||
:uri-pkl-config-java-Converter: {uri-pkl-config-java-main-sources}/mapper/Converter.java
|
||||
:uri-pkl-config-java-ConverterFactory: {uri-pkl-config-java-main-sources}/mapper/ConverterFactory.java
|
||||
:uri-pkl-config-java-PObjectToObjectByCtorTestJava: {uri-pkl-config-java-test-sources}/mapper/PObjectToObjectByCtorTest.java
|
||||
:uri-pkl-config-java-PObjectToObjectByCtorTestPkl: {uri-pkl-config-java-test-resources}/mapper/PObjectToObjectByCtorTest.pkl
|
||||
|
||||
The _pkl-config-java_ library builds upon xref:pkl-core:index.adoc[pkl-core].
|
||||
It offers a higher-level API specifically designed for consuming application runtime configuration.
|
||||
|
||||
== Installation
|
||||
|
||||
The _pkl-config-java_ library is available {uri-pkl-config-java-maven-module}[from Maven Central].
|
||||
It requires Java 11 or higher.
|
||||
|
||||
=== Gradle
|
||||
|
||||
To use the library in a Gradle project, declare the following dependency:
|
||||
|
||||
[tabs]
|
||||
====
|
||||
Groovy::
|
||||
+
|
||||
.build.gradle
|
||||
[source,groovy,subs="+attributes"]
|
||||
----
|
||||
dependencies {
|
||||
compile "org.pkl-lang:pkl-config-java:{pkl-artifact-version}"
|
||||
}
|
||||
|
||||
ifndef::is-release-build[]
|
||||
repositories {
|
||||
maven { url "{uri-sonatype}" }
|
||||
}
|
||||
endif::[]
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
.build.gradle.kts
|
||||
[source,kotlin,subs="+attributes"]
|
||||
----
|
||||
dependencies {
|
||||
compile("org.pkl-lang:pkl-config-java:{pkl-artifact-version}")
|
||||
}
|
||||
|
||||
ifndef::is-release-build[]
|
||||
repositories {
|
||||
maven { url = uri("{uri-sonatype}") }
|
||||
}
|
||||
endif::[]
|
||||
----
|
||||
====
|
||||
|
||||
Unlike `pkl-config-java`, `pkl-config-java__-all__` is a fat Jar with renamed third-party packages to avoid version conflicts.
|
||||
|
||||
=== Maven
|
||||
|
||||
To use the library in a Maven project, declare the following dependency:
|
||||
|
||||
.pom.xml
|
||||
[source,xml,subs="+attributes"]
|
||||
----
|
||||
<project>
|
||||
<dependency>
|
||||
<groupId>org.pkl-lang</groupId>
|
||||
<artifactId>pkl-config-java</artifactId>
|
||||
<version>{pkl-artifact-version}</version>
|
||||
</dependency>
|
||||
ifndef::is-release-build[]
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>sonatype-s01</id>
|
||||
<name>Sonatype S01</name>
|
||||
<url>{uri-sonatype}</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
endif::[]
|
||||
</project>
|
||||
----
|
||||
|
||||
Unlike `pkl-config-java`, `pkl-config-java__-all__` is a fat Jar with renamed third-party packages to avoid version conflicts.
|
||||
|
||||
== Usage
|
||||
|
||||
=== Consuming Configuration
|
||||
|
||||
The {uri-pkl-config-java-ConfigEvaluator}[`ConfigEvaluator`] class loads and evaluates Pkl modules.
|
||||
If evaluation succeeds, a {uri-pkl-config-java-Config}[`Config`] object is returned.
|
||||
Otherwise, an {uri-pkl-core-EvalException}[`EvalException`] with error details is thrown.
|
||||
|
||||
The returned `Config` object represents the root of the Pkl configuration tree.
|
||||
Intermediate and leaf nodes are also represented as `Config` objects.
|
||||
|
||||
`Config` objects offer methods to
|
||||
|
||||
* convert their Pkl value to a Java value of the specified type.
|
||||
* navigate to child nodes.
|
||||
|
||||
Let's see this in action:
|
||||
|
||||
[[config-evaluator-java-example]]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{examplesdir}/JavaConfigExample.java[tags=usage]
|
||||
----
|
||||
<1> Create a preconfigured `ConfigEvaluator`.
|
||||
To create a customized evaluator, start from `ConfigEvaluatorBuilder.preconfigured()` or `ConfigEvaluatorBuilder.unconfigured()`.
|
||||
The evaluator should be closed once it is no longer needed.
|
||||
In this example, this is done with a try-with-resources statement.
|
||||
Note that objects returned by the evaluator remain valid after calling `close()`.
|
||||
<2> Evaluate the given text.
|
||||
Other `evaluate` methods read from files, URLs, and other sources.
|
||||
If evaluation fails, an {uri-pkl-core-EvalException}[`EvalException`] is thrown.
|
||||
<3> Navigate from the config root to its `"pigeon"` child.
|
||||
<4> Navigate from `"pigeon"` to `"age"` and get the latter's value as an `int`.
|
||||
If conversion to the requested type fails, a `ConversionException` is thrown.
|
||||
<5> Navigate from `"pigeon"` to `"diet"` and get the latter's value as a `List<String>`.
|
||||
Note the use of `JavaType.listOf()` for creating a parameterized type literal.
|
||||
Similar methods exist for sets, maps, and other generic types.
|
||||
|
||||
A `ConfigEvaluator` caches module sources and evaluation results.
|
||||
To clear the cache, for example to evaluate the same module again, close the evaluator and create a new one.
|
||||
|
||||
For a ready-to-go example with full source code,
|
||||
see link:{uri-config-java-example}[config-java] in the _pkl/pkl-examples_ repository.
|
||||
|
||||
[[object-mapping]]
|
||||
=== Object Mapping
|
||||
|
||||
When a `Config` object needs to convert its Pkl value to a Java value, it delegates the conversion to {uri-pkl-config-java-ValueMapper}[`ValueMapper`].
|
||||
`ValueMapper` can convert an entire `PModule` or any part thereof.
|
||||
|
||||
A `ValueMapper` instance can be configured with many different Pkl-to-Java value conversions.
|
||||
`ValueMapper.preconfigured()` creates an instance configured with conversions from Pkl values to:
|
||||
|
||||
* Number types
|
||||
* Strings
|
||||
* Enums
|
||||
* Collections
|
||||
* Arrays
|
||||
* `java.util.Optional`
|
||||
* `java.time.Duration`
|
||||
* `java.net.URI/URL`
|
||||
* etc.
|
||||
|
||||
Additionally, a preconfigured `ValueMapper` instance can convert Pkl objects to Java objects with equally named properties that are settable through a constructor.
|
||||
This conversion works as follows:
|
||||
|
||||
. Find the Java class constructor with the highest number of parameters.
|
||||
. Match constructor parameters with Pkl object properties by name.
|
||||
+
|
||||
Unmatched constructor parameters result in a conversion error.
|
||||
Unmatched Pkl object properties are ignored.
|
||||
+
|
||||
. Convert each Pkl property value to the corresponding constructor parameter's type.
|
||||
. Invoke the constructor.
|
||||
|
||||
The Pkl object's runtime type is irrelevant to this conversion.
|
||||
Hence, typed and dynamic Pkl objects are equally supported.
|
||||
|
||||
To perform this conversion, `ValueMapper` needs a way to obtain the Java constructor's parameter names.
|
||||
They need to be provided in one of the following ways:
|
||||
|
||||
* Annotate constructor with `java.beans.ConstructorProperties`.
|
||||
* Annotate parameters with {uri-pkl-config-java-Named}[`Named`].
|
||||
* Annotate parameters with `javax.inject.Named`.
|
||||
* Set the Java compiler flag `-parameters`.
|
||||
|
||||
For a complete object mapping example, see:
|
||||
|
||||
* {uri-pkl-config-java-PObjectToObjectByCtorTestJava}[`PObjectToObjectByCtorTest.java`]
|
||||
|
||||
TIP: Together with xref:java-binding:codegen.adoc[code generation], object mapping provides a complete solution for consuming Pkl configuration as statically typed Java objects.
|
||||
Java code never drifts from the configuration structure defined in Pkl, and the entire configuration tree can be code-completed in Java IDEs.
|
||||
|
||||
==== Value Conversions
|
||||
|
||||
The Pkl-to-Java value conversions that ship with the library are defined in {uri-pkl-config-java-Conversions}[`Conversions`] (for individual conversions) and {uri-pkl-config-java-ConverterFactories}[`ConverterFactories`] (for families of conversions).
|
||||
To implement and register your own conversions, follow these steps:
|
||||
|
||||
. For conversions from a single source type to a single target type, implement a {uri-pkl-config-java-Conversion}[`Conversion`].
|
||||
+
|
||||
Example: `Conversions.pStringToCharacter` converts a single-character `pkl.base#String` to `java.lang.Character`.
|
||||
|
||||
. For conversions from one or multiple source types to one or multiple target types, implement a {uri-pkl-config-java-ConverterFactory}[`ConverterFactory`].
|
||||
+
|
||||
Example: `ConverterFactories.pCollectionToCollection` converts any `pkl.base#Collection` to any implementation of `java.util.Collection<E>`, for any `E`.
|
||||
+
|
||||
Converter factories are called once per combination of source type and (possibly parameterized) target type.
|
||||
The returned `Converter`s are cached.
|
||||
|
||||
. Create a `ValueMapperBuilder`, add all desired conversions, and build a `ValueMapper`.
|
||||
|
||||
. Either use the `ValueMapper` directly, or connect it to a `ConfigEvaluator` through `ConfigEvaluatorBuilder`.
|
||||
|
||||
== Further Information
|
||||
|
||||
Refer to the Javadoc and sources published with the library, or browse the library's {uri-pkl-config-java-main-sources}[main] and {uri-pkl-config-java-test-sources}[test] sources.
|
||||
23
docs/modules/java-binding/partials/cli-codegen-options.adoc
Normal file
23
docs/modules/java-binding/partials/cli-codegen-options.adoc
Normal file
@@ -0,0 +1,23 @@
|
||||
.--indent
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `" "` (two spaces) +
|
||||
Example: `"\t"` (one tab) +
|
||||
The characters to use for indenting generated source code.
|
||||
====
|
||||
|
||||
.-o, --output-dir
|
||||
[%collapsible]
|
||||
====
|
||||
Default: (not set) +
|
||||
Example: `generated/` +
|
||||
The directory where generated source code is placed.
|
||||
Relative paths are resolved against the working directory.
|
||||
====
|
||||
|
||||
.--generate-spring-boot
|
||||
[%collapsible]
|
||||
====
|
||||
Default: (not set) +
|
||||
Flag that indicates to generate config classes for use with Spring Boot.
|
||||
====
|
||||
33
docs/modules/kotlin-binding/examples/KotlinConfigExample.kt
Normal file
33
docs/modules/kotlin-binding/examples/KotlinConfigExample.kt
Normal file
@@ -0,0 +1,33 @@
|
||||
@file:Suppress("UNUSED_VARIABLE")
|
||||
|
||||
import org.pkl.config.java.ConfigEvaluator
|
||||
import org.pkl.config.kotlin.forKotlin
|
||||
import org.pkl.config.kotlin.to
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
// the pkl/pkl-examples repo has a similar example
|
||||
class KotlinConfigExample {
|
||||
@Test
|
||||
fun usage() {
|
||||
// tag::usage[]
|
||||
val evaluator = ConfigEvaluator.preconfigured().forKotlin() // <1>
|
||||
val config = evaluator.use { // <2>
|
||||
it.evaluateText("""pigeon { age = 5; diet = "Seeds" }""")
|
||||
}
|
||||
val pigeon = config["pigeon"] // <3>
|
||||
val age = pigeon["age"].to<Int>() // <4>
|
||||
val hobbies = pigeon["diet"].to<List<String>>() // <5>
|
||||
// end::usage[]
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nullable() {
|
||||
// tag::nullable[]
|
||||
val evaluator = ConfigEvaluator.preconfigured().forKotlin()
|
||||
val config = evaluator.use {
|
||||
it.evaluateText("name = null") // <1>
|
||||
}
|
||||
val name = config["name"].to<String?>() // <2>
|
||||
// end::nullable[]
|
||||
}
|
||||
}
|
||||
130
docs/modules/kotlin-binding/pages/codegen.adoc
Normal file
130
docs/modules/kotlin-binding/pages/codegen.adoc
Normal file
@@ -0,0 +1,130 @@
|
||||
= Kotlin Code Generator
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
:uri-pkl-codegen-kotlin-maven-module: {uri-maven-docsite}/artifact/org.pkl-lang/pkl-codegen-kotlin
|
||||
|
||||
The Kotlin source code generator reads Pkl classes and generates corresponding Kotlin classes with equally named properties.
|
||||
|
||||
Together with xref:java-binding:pkl-config-java.adoc#object-mapping[Object Mapping], code generation provides a complete solution for consuming Pkl configuration as statically typed Kotlin objects.
|
||||
Kotlin code never drifts from the configuration structure defined in Pkl, and the entire configuration tree can be code-completed in Kotlin IDEs.
|
||||
|
||||
== Installation
|
||||
|
||||
The code generator is offered as Gradle plugin, Java library, and CLI.
|
||||
|
||||
=== Gradle Plugin
|
||||
|
||||
See xref:pkl-gradle:index.adoc#installation[Installation] in the Gradle plugin chapter.
|
||||
|
||||
[[install-library]]
|
||||
=== Java Library
|
||||
|
||||
The `pkl-codegen-kotlin` library is available {uri-pkl-codegen-kotlin-maven-module}[from Maven Central].
|
||||
It requires Java 8 or higher and Kotlin 1.3 or higher.
|
||||
|
||||
==== Gradle
|
||||
|
||||
To use the library in a Gradle project, declare the following dependency:
|
||||
|
||||
[tabs]
|
||||
====
|
||||
Groovy::
|
||||
+
|
||||
.build.gradle
|
||||
[source,groovy,subs="+attributes"]
|
||||
----
|
||||
dependencies {
|
||||
compile "org.pkl-lang:pkl-config-kotlin:{pkl-artifact-version}"
|
||||
}
|
||||
|
||||
ifndef::is-release-build[]
|
||||
repositories {
|
||||
maven { url "{uri-sonatype}" }
|
||||
}
|
||||
endif::[]
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
.build.gradle.kts
|
||||
[source,kotlin,subs="+attributes"]
|
||||
----
|
||||
dependencies {
|
||||
compile("org.pkl-lang:pkl-config-kotlin:{pkl-artifact-version}")
|
||||
}
|
||||
|
||||
ifndef::is-release-build[]
|
||||
repositories {
|
||||
maven { url = uri("{uri-sonatype}") }
|
||||
}
|
||||
endif::[]
|
||||
----
|
||||
====
|
||||
|
||||
==== Maven
|
||||
|
||||
To use the library in a Maven project, declare the following dependency:
|
||||
|
||||
.pom.xml
|
||||
[source,xml,subs="+attributes"]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.pkl-lang</groupId>
|
||||
<artifactId>pkl-codegen-kotlin</artifactId>
|
||||
<version>{pkl-artifact-version}</version>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
[[install-cli]]
|
||||
=== CLI
|
||||
|
||||
The CLI is bundled with the library.
|
||||
As we do not currently ship the CLI as a self-contained Jar, we recommend to provision it with a Maven compatible build tool as shown in <<install-library>>.
|
||||
|
||||
[[usage]]
|
||||
== Usage
|
||||
|
||||
The code generator is offered as Gradle plugin, Java library, and CLI.
|
||||
|
||||
=== Gradle Plugin
|
||||
|
||||
See xref:pkl-gradle:index.adoc#kotlin-code-gen[Kotlin Code Generation] in the Gradle plugin chapter.
|
||||
|
||||
=== Java Library
|
||||
|
||||
The library offers two APIs: a high-level API that corresponds to the CLI, and a lower-level API that provides additional features and control.
|
||||
The entry points for these APIs are `org.pkl.codegen.kotlin.CliKotlinCodeGenerator` and `org.pkl.codegen.kotlin.KotlinCodeGenerator`, respectively.
|
||||
For more information, refer to the KDoc documentation.
|
||||
|
||||
=== CLI
|
||||
|
||||
As mentioned in <<install-cli,Installation>>, the CLI is bundled with the library.
|
||||
To run the CLI, execute the library Jar or its `org.pkl.codegen.kotlin.Main` main class.
|
||||
|
||||
*Synopsis:* `java -cp <classpath> -jar pkl-codegen-kotlin.jar [<options>] <modules>`
|
||||
|
||||
`<modules>`::
|
||||
The absolute or relative URIs of the modules to generate classe for.
|
||||
Relative URIs are resolved against the working directory.
|
||||
|
||||
==== Options
|
||||
|
||||
.--generate-kdoc
|
||||
[%collapsible]
|
||||
====
|
||||
Default: (flag not set) +
|
||||
Flag that indicates to generate Kdoc based on doc comments for Pkl modules, classes, and properties.
|
||||
====
|
||||
|
||||
Common code generator options:
|
||||
|
||||
include::../../java-binding/partials/cli-codegen-options.adoc[]
|
||||
|
||||
Common CLI options:
|
||||
|
||||
include::../../pkl-cli/partials/cli-common-options.adoc[]
|
||||
|
||||
[[full-example]]
|
||||
== Full Example
|
||||
|
||||
For a ready-to-go example with full source code,
|
||||
see link:{uri-codegen-kotlin-example}[codegen-kotlin] in the _pkl/pkl-examples_ repository.
|
||||
3
docs/modules/kotlin-binding/pages/index.adoc
Normal file
3
docs/modules/kotlin-binding/pages/index.adoc
Normal file
@@ -0,0 +1,3 @@
|
||||
= Integration with Kotlin
|
||||
|
||||
Pkl provides rich integration with Kotlin. Our integration allows you to embed the Pkl runtime into your Kotlin application, and also provides code generation for from Pkl source code.
|
||||
117
docs/modules/kotlin-binding/pages/pkl-config-kotlin.adoc
Normal file
117
docs/modules/kotlin-binding/pages/pkl-config-kotlin.adoc
Normal file
@@ -0,0 +1,117 @@
|
||||
= pkl-config-kotlin Library
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
:uri-pkl-config-kotlin-maven-module: {uri-maven-docsite}/artifact/org.pkl-lang/pkl-config-kotlin
|
||||
:uri-pkl-config-kotlin-main-sources: {uri-github-tree}/pkl-config-kotlin/src/main/kotlin/org/pkl/kotlin
|
||||
:uri-pkl-config-kotlin-test-sources: {uri-github-tree}/pkl-config-kotlin/src/test/kotlin/org/pkl/kotlin
|
||||
:uri-pkl-config-kotlin-ConverterFactories: {uri-pkl-config-kotlin-main-sources}/ConverterFactories.kt
|
||||
:uri-pkl-config-kotlin-ConfigExtensions: {uri-pkl-config-kotlin-main-sources}/ConfigExtensions.kt
|
||||
|
||||
The _pkl-config-kotlin_ library extends xref:java-binding:pkl-config-java.adoc[pkl-config-java] with Kotlin specific extension methods and object converters.
|
||||
We recommend that Kotlin projects depend on this library instead of _pkl-config-java_.
|
||||
|
||||
== Installation
|
||||
|
||||
The _pkl-config-kotlin_ library is available {uri-pkl-config-kotlin-maven-module}[from Maven Central].
|
||||
It requires Java 11 or higher and Kotlin 1.5 or higher.
|
||||
|
||||
=== Gradle
|
||||
|
||||
To use the library in a Gradle project, declare the following dependency:
|
||||
|
||||
[tabs]
|
||||
====
|
||||
Groovy::
|
||||
+
|
||||
.build.gradle
|
||||
[source,groovy,subs="+attributes"]
|
||||
----
|
||||
dependencies {
|
||||
compile "org.pkl-lang:pkl-config-kotlin:{pkl-artifact-version}"
|
||||
}
|
||||
|
||||
ifndef::is-release-build[]
|
||||
repositories {
|
||||
maven { url "{uri-sonatype}" }
|
||||
}
|
||||
endif::[]
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
.build.gradle.kts
|
||||
[source,kotlin,subs="+attributes"]
|
||||
----
|
||||
dependencies {
|
||||
compile("org.pkl-lang:pkl-config-kotlin:{pkl-artifact-version}")
|
||||
}
|
||||
|
||||
ifndef::is-release-build[]
|
||||
repositories {
|
||||
maven { url = uri("{uri-sonatype}") }
|
||||
}
|
||||
endif::[]
|
||||
----
|
||||
====
|
||||
|
||||
=== Maven
|
||||
|
||||
To use the library in a Maven project, declare the following dependency:
|
||||
|
||||
.pom.xml
|
||||
[source,xml,subs="+attributes"]
|
||||
----
|
||||
<project>
|
||||
<dependency>
|
||||
<groupId>org.pkl-lang</groupId>
|
||||
<artifactId>pkl-config-kotlin</artifactId>
|
||||
<version>{pkl-artifact-version}</version>
|
||||
</dependency>
|
||||
ifndef::is-release-build[]
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>sonatype-s01</id>
|
||||
<name>Sonatype S01</name>
|
||||
<url>{uri-sonatype}</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
endif::[]
|
||||
</project>
|
||||
----
|
||||
|
||||
== Usage
|
||||
|
||||
Below is the Kotlin version of the Java xref:java-binding:pkl-config-java.adoc#config-evaluator-java-example[ConfigEvaluator] example.
|
||||
Differences to the Java version are called out.
|
||||
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
include::{examplesdir}/KotlinConfigExample.kt[tags=usage]
|
||||
----
|
||||
<1> Use the `forKotlin()` method to preconfigure the builder with Kotlin specific conversions.
|
||||
In particular, `forKotlin()` eliminates the need to annotate constructor parameters of Kotlin classes and Kotlin data classes with `@Named`.
|
||||
<2> The evaluator should be closed once it is no longer needed.
|
||||
Here this is done with a Kotlin `use {}` expression.
|
||||
Any data returned by the evaluator before calling `close()` remains valid.
|
||||
<3> Navigate to the `"pigeon"` child.
|
||||
The subscript notation is shorthand for `config.get("pigeon")`.
|
||||
<4> Convert `"age"` to `Int` with the `Config.to()` extension method.
|
||||
The target type is provided as a type argument.
|
||||
Always use `Config.to()` instead of `Config.as()` in Kotlin.
|
||||
<5> `Config.to()` makes conversions to parameterized types straightforward:
|
||||
`to<List<String>>()` instead of `as(JavaType.listOf(String::class.java))`.
|
||||
|
||||
For properties that are allowed to be `null`, convert to a nullable type:
|
||||
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
include::{examplesdir}/KotlinConfigExample.kt[tags=nullable]
|
||||
----
|
||||
<1> To indicate that `null` is an allowed value, convert to the nullable type `String?`.
|
||||
Converting to `String` would result in a `ConversionException`.
|
||||
|
||||
For a ready-to-go example with full source code,
|
||||
see link:{uri-config-kotlin-example}[config-kotlin] in the _pkl/pkl-examples_ repository.
|
||||
|
||||
== Further Information
|
||||
|
||||
Refer to the Javadoc and sources published with the library, or browse the library's {uri-pkl-config-kotlin-main-sources}[main] and {uri-pkl-config-kotlin-test-sources}[test] sources.
|
||||
5325
docs/modules/language-reference/pages/index.adoc
Normal file
5325
docs/modules/language-reference/pages/index.adoc
Normal file
File diff suppressed because it is too large
Load Diff
BIN
docs/modules/language-tutorial/images/pclHubRio.png
Normal file
BIN
docs/modules/language-tutorial/images/pclHubRio.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 124 KiB |
317
docs/modules/language-tutorial/pages/01_basic_config.adoc
Normal file
317
docs/modules/language-tutorial/pages/01_basic_config.adoc
Normal file
@@ -0,0 +1,317 @@
|
||||
= Basic Configuration
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
|
||||
In this first part of xref:index.adoc[the Pkl tutorial], you build familiarity with Pkl syntax and basic structure.
|
||||
You also learn different ways to invoke Pkl to produce different formats.
|
||||
|
||||
== Basic values
|
||||
|
||||
Consider the following example Pkl file.
|
||||
|
||||
[source,{pkl}]
|
||||
.intro.pkl
|
||||
----
|
||||
name = "Pkl: Configure your Systems in New Ways"
|
||||
attendants = 100
|
||||
isInteractive = true
|
||||
amountLearned = 13.37
|
||||
----
|
||||
|
||||
Running Pkl on this file gives
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
$ pkl eval /Users/me/tutorial/intro.pkl
|
||||
name = "Pkl: Configure your Systems in New Ways"
|
||||
attendants = 100
|
||||
isInteractive = true
|
||||
amountLearned = 13.37
|
||||
----
|
||||
|
||||
It may seem nothing happened.
|
||||
However, Pkl tells you that it _accepts the input_.
|
||||
In other words, you now know that `intro.pkl` does not contain any errors.
|
||||
|
||||
You can ask Pkl to print this configuration in a different format, using the `-f` option.
|
||||
For example, JSON:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
$ pkl eval -f json /Users/me/tutorial/intro.pkl
|
||||
{
|
||||
"name": "Pkl: Configure your Systems in New Ways",
|
||||
"attendants": 100,
|
||||
"isInteractive": true,
|
||||
"amountLearned": 13.37
|
||||
}
|
||||
----
|
||||
|
||||
Or _PropertyList_ format:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
$ pkl eval -f plist /Users/me/tutorial/intro.pkl
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Pkl: Configure your Systems in New Ways</string>
|
||||
<key>attendants</key>
|
||||
<integer>100</integer>
|
||||
<key>isInteractive</key>
|
||||
<true/>
|
||||
<key>amountLearned</key>
|
||||
<real>13.37</real>
|
||||
</dict>
|
||||
</plist>
|
||||
----
|
||||
|
||||
Notice that Pkl generated `<string>`, `<integer>`, `<true/>` and `<real>` for the values in your configuration.
|
||||
This means it has _both_ correctly derived the types of the literal values _and_ translated those types to the corresponding elements in the PropertyList.
|
||||
xref:03_writing_a_template.adoc[Part III] goes into types in more detail.
|
||||
|
||||
|
||||
== Structure: Classes, objects, modules
|
||||
|
||||
A configuration often requires more than just basic values.
|
||||
Typically, you need some kind of (hierarchical) structure.
|
||||
Pkl provides _immutable objects_ for this.
|
||||
|
||||
Objects have three kinds of members: properties, elements and entries.
|
||||
First, look at the syntax for objects and their members.
|
||||
|
||||
=== Properties
|
||||
|
||||
[source,{pkl}]
|
||||
.simpleObjectWithProperties.pkl
|
||||
----
|
||||
bird { // <1>
|
||||
name = "Common wood pigeon" // <2>
|
||||
diet = "Seeds"
|
||||
taxonomy { // <3>
|
||||
species = "Columba palumbus"
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> This _defines_ `bird` to be an object
|
||||
<2> For primitive values, Pkl has the `=` syntax (more on this later).
|
||||
<3> Just like `bird {`, but to show that objects can be nested.
|
||||
|
||||
This defines an object called `bird` with three _named properties_: `name`, `diet`, and `taxonomy`.
|
||||
The first two of these are strings, but `taxonomy` is another object.
|
||||
This means properties in an object can have different types and objects can be nested.
|
||||
|
||||
=== Elements
|
||||
|
||||
Of course, you don't always have names for every individual structure in your configuration.
|
||||
What if you want "just a bunch of things" without knowing how many?
|
||||
Pkl offers _elements_ for this purpose.
|
||||
Elements are object members, just like properties.
|
||||
Where you index properties by their name, you index elements by an integer.
|
||||
You can think of an object that only contains elements as _array_.
|
||||
Much like arrays in many languages, you can use square brackets to access an element, for example, `myObject[42]`.
|
||||
|
||||
You write an element, by writing only an expression.
|
||||
Pkl derives the index from the number of elements already in the object.
|
||||
For example:
|
||||
|
||||
[source,{pkl}]
|
||||
.simpleObjectsWithElements.pkl
|
||||
----
|
||||
exampleObjectWithJustIntElements {
|
||||
100 // <1>
|
||||
42
|
||||
}
|
||||
|
||||
exampleObjectWithMixedElements {
|
||||
"Bird Breeder Conference"
|
||||
(2000 + 23) // <2>
|
||||
exampleObjectWithJustIntElements // <3>
|
||||
}
|
||||
----
|
||||
<1> When you write only the value (without a name), you describe an _element_.
|
||||
<2> Elements don't have to be literal values; they can be arbitrary _expressions_.
|
||||
<3> Elements can really be _any_ value, not just primitive values.
|
||||
|
||||
=== Entries
|
||||
|
||||
Objects can have one more kind of member; _entries_.
|
||||
Like a _property_, an _entry_ is "named" (technically _keyed_).
|
||||
Unlike a property, the name does not need to be known at declaration time.
|
||||
Of course, we need a syntax to tell entries apart from properties.
|
||||
You write entry "names" by enclosing them in square brackets ("names" is quoted, because the names do not need to be strings; any value can index entries).
|
||||
|
||||
[source,{pkl}]
|
||||
.simpleObjectsWithEntries.pkl
|
||||
----
|
||||
pigeonShelter {
|
||||
["bird"] { // <1>
|
||||
name = "Common wood pigeon"
|
||||
diet = "Seeds"
|
||||
taxonomy {
|
||||
species = "Columba palumbus"
|
||||
}
|
||||
}
|
||||
["address"] = "355 Bird St." // <2>
|
||||
}
|
||||
|
||||
birdCount {
|
||||
[pigeonShelter] = 42 // <3>
|
||||
}
|
||||
----
|
||||
<1> The difference with properties is the notation of the key: `[<expression>]`.
|
||||
<2> As with properties, entries can be primitive values or objects.
|
||||
<3> Any object can be used as a key for an entry.
|
||||
|
||||
|
||||
=== Mixed members
|
||||
|
||||
In the examples so far, you have seen objects with properties, object with elements and object with entries.
|
||||
These object members can be freely mixed.
|
||||
|
||||
[source,{pkl}]
|
||||
.mixedObject.pkl
|
||||
----
|
||||
mixedObject {
|
||||
name = "Pigeon"
|
||||
lifespan = 8
|
||||
"wing"
|
||||
"claw"
|
||||
["wing"] = "Not related to the _element_ \"wing\""
|
||||
42
|
||||
extinct = false
|
||||
[false] {
|
||||
description = "Construed object example"
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Notice, how properties (`name`, `lifespan` and `extinct`), elements (`"wing"`, `"claw"`, `42`) and entries (`"wing"`, `false`) are mixed together in this one object.
|
||||
You don't have to order them by kind, and you don't require (other) special syntax.
|
||||
|
||||
=== Collections
|
||||
|
||||
This free-for-all mixing of object members can become confusing.
|
||||
Also, target formats are often considerably more restrictive.
|
||||
In the following example, you see what happens when you try to produce JSON from `mixedObject`:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
$ pkl eval -f json /Users/me/tutorial/mixedObject.pkl
|
||||
–– Pkl Error ––
|
||||
Cannot render object with both properties/entries and elements as JSON.
|
||||
Object: "Pigeon"
|
||||
|
||||
89 | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (https://github.com/apple/pkl/blob/0.24.0/stdlib/base.pkl#L90)
|
||||
----
|
||||
|
||||
This is why Pkl has two special types of object, namely _listings_, which contain _exclusively_ elements, and _mappings_, which contain _exclusively_ entries.
|
||||
Both listings and mappings _are_ "just objects," so, they don't require syntax besides that of objects:
|
||||
|
||||
[source,{pkl}]
|
||||
.collections.pkl
|
||||
----
|
||||
birds { // <1>
|
||||
"Pigeon"
|
||||
"Parrot"
|
||||
"Barn owl"
|
||||
"Falcon"
|
||||
}
|
||||
|
||||
habitats { // <2>
|
||||
["Pigeon"] = "Streets"
|
||||
["Parrot"] = "Parks"
|
||||
["Barn owl"] = "Forests"
|
||||
["Falcon"] = "Mountains"
|
||||
}
|
||||
----
|
||||
<1> A listing containing four elements.
|
||||
<2> A mapping containing four entries.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
_Technically_, the correct way to define `birds` and `habitats` is by using `new Listing {...}` and `new Mapping {...}` explicitly.
|
||||
You will see what these mean in part xref:03_writing_a_template.adoc[three] of this tutorial.
|
||||
====
|
||||
|
||||
When you render _this_ configuration as JSON, everything works:
|
||||
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"birds": [
|
||||
"Pigeon",
|
||||
"Parrot",
|
||||
"Barn owl",
|
||||
"Falcon"
|
||||
],
|
||||
"habitats": {
|
||||
"Pigeon": "Streets",
|
||||
"Parrot": "Parks",
|
||||
"Barn owl": "Forests",
|
||||
"Falcon": "Mountains"
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Notice particularly, that you rendered the listing as a JSON _array_.
|
||||
When you index the listing with an integer, you're referring to the element inside the listing at the corresponding position (starting from `0`).
|
||||
For example:
|
||||
|
||||
[source,{pkl}]
|
||||
.indexedListing.pkl
|
||||
----
|
||||
birds {
|
||||
"Pigeon"
|
||||
"Parrot"
|
||||
"Barn owl"
|
||||
"Falcon"
|
||||
}
|
||||
|
||||
relatedToSnowOwl = birds[2]
|
||||
----
|
||||
results in
|
||||
[source,{pkl}]
|
||||
----
|
||||
birds {
|
||||
"Pigeon"
|
||||
"Parrot"
|
||||
"Barn owl"
|
||||
"Falcon"
|
||||
}
|
||||
relatedToSnowOwl = "Barn owl"
|
||||
----
|
||||
|
||||
== Exercises
|
||||
|
||||
1. Given the following JSON snippet (taken from W3C examples), write the `.pkl` file that produces this JSON:
|
||||
|
||||
+
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"name": "Common wood pigeon",
|
||||
"lifespan": 8,
|
||||
"friends": {
|
||||
"bird1": "Parrot",
|
||||
"bird2": "Albatross",
|
||||
"bird3": "Falcon"
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
2. For some reason, we decide we no longer need the birdX names of the different birds; we just need them as an array.
|
||||
Change your solution to the previous question to produce the following JSON result:
|
||||
|
||||
+
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"name": "Common wood pigeon",
|
||||
"lifespan": 8,
|
||||
"birds": ["Parrot", "Barn owl", "Falcon"]
|
||||
}
|
||||
----
|
||||
@@ -0,0 +1,438 @@
|
||||
= Filling out a Template
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
|
||||
In this second part of xref:index.adoc[the Pkl tutorial], you will learn how to write one (part of a) configuration in terms of another.
|
||||
You will also find and fill out an existing _template_.
|
||||
|
||||
== Composing configurations
|
||||
=== Amending
|
||||
|
||||
The central mechanism in Pkl for expressing one (part of a) configuration in terms of another is _amending_.
|
||||
Consider the following example.
|
||||
|
||||
[source,{pkl}]
|
||||
.amendingObjects.pkl
|
||||
----
|
||||
bird {
|
||||
name = "Pigeon"
|
||||
diet = "Seeds"
|
||||
taxonomy {
|
||||
kingdom = "Animalia"
|
||||
clade = "Dinosauria"
|
||||
order = "Columbiformes"
|
||||
}
|
||||
}
|
||||
|
||||
parrot = (bird) {
|
||||
name = "Parrot"
|
||||
diet = "Berries"
|
||||
taxonomy {
|
||||
order = "Psittaciformes"
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Parrot and Pigeon have nearly identical properties.
|
||||
They only differ in their name and taxonomy, so if you have already written out `bird`, you can say that `parrot` is just like `pigeon` except `name` is `"Parrot"`, diet is `"Berries"` the `taxonomy.order` is `"Psittaciformes"`.
|
||||
When you run this, Pkl expands everything fully.
|
||||
|
||||
[source,{pkl}]
|
||||
----
|
||||
bird {
|
||||
name = "Common wood pigeon"
|
||||
diet = "Seeds"
|
||||
taxonomy {
|
||||
kingdom = "Animalia"
|
||||
clade = "Dinosauria"
|
||||
order = "Columbiformes"
|
||||
}
|
||||
}
|
||||
parrot {
|
||||
name = "Parrot"
|
||||
diet = "Berries"
|
||||
taxonomy {
|
||||
kingdom = "Animalia"
|
||||
clade = "Dinosauria"
|
||||
order = "Psittaciformes"
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
_Amending_ does not allow us to _add_ properties to the (typed) object we are amending.
|
||||
The xref:03_writing_a_template.adoc[next part of the tutorial] discusses types in more detail.
|
||||
There, you see that amending _never changes the type_ of the object.
|
||||
====
|
||||
|
||||
You can also amend nested objects.
|
||||
This allows you to only describe the difference with the outermost object for arbitrarily deeply nested structures.
|
||||
Consider the following example.
|
||||
|
||||
[source,{pkl}]
|
||||
.nestedAmends.pkl
|
||||
----
|
||||
woodPigeon {
|
||||
name = "Common wood pigeon"
|
||||
diet = "Seeds"
|
||||
taxonomy {
|
||||
species = "Columba palumbus"
|
||||
}
|
||||
}
|
||||
|
||||
stockPigeon = (woodPigeon) {
|
||||
name = "Stock pigeon"
|
||||
taxonomy { // <1>
|
||||
species = "Columba oenas"
|
||||
}
|
||||
}
|
||||
|
||||
dodo = (stockPigeon) { // <2>
|
||||
name = "Dodo"
|
||||
extinct = true // <3>
|
||||
taxonomy {
|
||||
species = "Raphus cucullatus"
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> This amends `species`, _as it occurs in_ `stockPigeon`.
|
||||
<2> Amended objects can, themselves, be amended.
|
||||
<3> New fields can be added to objects when amending.
|
||||
|
||||
Notice how you only have to change `taxonomy.species`.
|
||||
In this example, `bird.taxonomy` has `kingdom`, `clade`, `order` and `species`.
|
||||
You are amending `stockPigeon`, to define `woodPigeon`.
|
||||
They have the same `taxonomy`, except for `species`.
|
||||
This notation says that everything in `taxonomy` should be what it is in the object you are amending (`stockPigeon`), except for `species`, which should be `"Columba palumbus"` .
|
||||
|
||||
For the input above, Pkl produces the following output.
|
||||
[source,{pkl}]
|
||||
----
|
||||
woodPigeon {
|
||||
name = "Common wood pigeon"
|
||||
diet = "Seeds"
|
||||
taxonomy {
|
||||
species = "Columba palumbus"
|
||||
}
|
||||
}
|
||||
stockPigeon {
|
||||
name = "Stock pigeon"
|
||||
diet = "Seeds"
|
||||
taxonomy {
|
||||
species = "Columba oenas"
|
||||
}
|
||||
}
|
||||
dodo {
|
||||
name = "Dodo"
|
||||
diet = "Seeds"
|
||||
extinct = true
|
||||
taxonomy {
|
||||
species = "Raphus cucullatus"
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
So far, you have only amended _properties_.
|
||||
Since you refer to them by name, it makes sense that you "overwrite" the value from the object you're amending.
|
||||
What if you include _elements_ or _entries_ in an amends expression?
|
||||
|
||||
[source,{pkl}]
|
||||
.amendElementsAndEntries.pkl
|
||||
----
|
||||
favoriteFoods {
|
||||
"red berries"
|
||||
"blue berries"
|
||||
["Barn owl"] {
|
||||
"mice"
|
||||
}
|
||||
}
|
||||
|
||||
adultBirdFoods = (favoriteFoods) {
|
||||
[1] = "pebbles" // <1>
|
||||
"worms" // <2>
|
||||
["Falcon"] { // <3>
|
||||
"insects"
|
||||
"amphibians"
|
||||
}
|
||||
["Barn owl"] { // <4>
|
||||
"fish"
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Explicitly amending _by index_ replaces the element at that index.
|
||||
<2> Without explicit indices, Pkl can't know which element to overwrite, so, instead, it _adds_ an element to the object you're amending.
|
||||
<3> When you write "new" entries (using a key that does not occur in the object you're amending), Pkl also _adds_ them.
|
||||
<4> When you write an entry using a key that exists, this notation amends its value.
|
||||
|
||||
Pkl can't know which of the `favoriteFoods` to overwrite only by their _value_.
|
||||
When you want to _replace_ an element, you have to explicitly amend the element at a specific index.
|
||||
This is why a "plain" element in an amends expression is _added_ to the object being amended.
|
||||
Result:
|
||||
|
||||
[source,{pkl}]
|
||||
----
|
||||
favoriteFoods {
|
||||
["Barn owl"] {
|
||||
"mice"
|
||||
}
|
||||
"red berries"
|
||||
"blue berries"
|
||||
}
|
||||
adultBirdFoods {
|
||||
["Barn owl"] {
|
||||
"mice"
|
||||
"fish"
|
||||
}
|
||||
"red berries"
|
||||
"pebbles"
|
||||
["Falcon"] {
|
||||
"insects"
|
||||
"amphibians"
|
||||
}
|
||||
"worms"
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
=== Modules
|
||||
|
||||
A `.pkl` file describes a _module_.
|
||||
Modules are objects that can be referred to from other modules.
|
||||
Going back to the example above, you can write `parrot` as a separate module.
|
||||
|
||||
[source,{pkl}]
|
||||
.pigeon.pkl
|
||||
----
|
||||
name = "Common wood pigeon"
|
||||
diet = "Seeds"
|
||||
taxonomy {
|
||||
species = "Columba palumbus"
|
||||
}
|
||||
----
|
||||
|
||||
You can `import` this module and express `parrot` like you did before.
|
||||
|
||||
[source,{pkl}]
|
||||
.parrot.pkl
|
||||
----
|
||||
import "pigeon.pkl" // <1>
|
||||
|
||||
parrot = (pigeon) {
|
||||
name = "Great green macaw"
|
||||
diet = "Berries"
|
||||
species {
|
||||
species = "Ara ambiguus"
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Importing `foo.pkl` creates the object `foo`, so you can refer to `pigeon` in this code, like you did before.
|
||||
|
||||
If you run Pkl on both, you will see that it works.
|
||||
Looking at the result, however, you see a (possibly) unexpected difference.
|
||||
|
||||
[source,{pkl}]
|
||||
----
|
||||
$ pkl eval /Users/me/tutorial/pigeon.pkl
|
||||
name = "Common wood pigeon""
|
||||
diet = "Seeds"
|
||||
taxonomy {
|
||||
species = "Columba palumbus"
|
||||
}
|
||||
|
||||
$ pkl eval /Users/me/tutorial/parrot.pkl
|
||||
parrot {
|
||||
name = "Great green macaw"
|
||||
diet = "Berries"
|
||||
taxonomy {
|
||||
species = "Ara ambiguus"
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The object `pigeon` is "spread" in the top-level, while `parrot` is a nested and named object.
|
||||
This is because writing `parrot {...}` defines an object property _in_ the "current" module.
|
||||
|
||||
In order to say that "this module is an object, amended from the `pigeon` module," you use an _amends clause_.
|
||||
|
||||
[source,{pkl}]
|
||||
.parrot.pkl
|
||||
----
|
||||
amends "pigeon.pkl" // <1>
|
||||
|
||||
name = "Great green macaw"
|
||||
----
|
||||
<1> "This" module is the same as `"pigeon.pkl"`, except for what is in the remainder of the file.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
As a first intuition, think of "amending a module" as "filling out a form."
|
||||
====
|
||||
|
||||
== Amending templates
|
||||
|
||||
A Pkl file can be either a _template_ or a _"normal" module_.
|
||||
This terminology describes the _intended use_ of the module and doesn't imply anything about its structure.
|
||||
In other words: just by looking at Pkl code, you can't tell whether it is a template or a "normal" module.
|
||||
|
||||
[source,{pkl}]
|
||||
.acmecicd.pkl
|
||||
----
|
||||
module acmecicd
|
||||
|
||||
class Pipeline {
|
||||
name: String(nameRequiresBranchName)?
|
||||
|
||||
hidden nameRequiresBranchName = (_) ->
|
||||
if (branchName == null)
|
||||
throw("Pipelines that set a 'name' must also set a 'branchName'.")
|
||||
else true
|
||||
|
||||
branchName: String?
|
||||
}
|
||||
|
||||
timeout: Int(this >= 3)
|
||||
|
||||
pipelines: Listing<Pipeline>
|
||||
|
||||
output {
|
||||
renderer = new YamlRenderer {}
|
||||
}
|
||||
----
|
||||
|
||||
Remember that amending is like filling out a form.
|
||||
That's exactly what you're doing here; you're filling out "work order forms".
|
||||
|
||||
Next, add a time-out of one minute for your job.
|
||||
|
||||
[source,{pkl}]
|
||||
.cicd.pkl
|
||||
----
|
||||
amends "acmecicd.pkl"
|
||||
|
||||
timeout = 1
|
||||
----
|
||||
Unfortunately, Pkl does not accept this configuration and provides a rather elaborate error message:
|
||||
[source,plain]
|
||||
----
|
||||
–– Pkl Error –– // <1>
|
||||
Type constraint `this >= 3` violated. // <2>
|
||||
Value: 1 // <3>
|
||||
|
||||
225 | timeout: Int(this >= 3)? // <4>
|
||||
^^^^^^^^^
|
||||
at acmecicd#timeout (file:///Users/me/tutorial/acmecicd.pkl, line 8)
|
||||
|
||||
3 | timeout = 1 // <5>
|
||||
^
|
||||
at cicd#timeout (file:///Users/me/tutorial/cicd.pkl, line 3)
|
||||
|
||||
90 | text = renderer.renderDocument(value) // <6>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (https://github.com/apple/pkl/blob/e4d8c882d/stdlib/base.pkl#L90)
|
||||
|
||||
----
|
||||
<1> Pkl found an error.
|
||||
<2> Which error Pkl found.
|
||||
<3> What the offending value is.
|
||||
<4> Where Pkl found its expectation (line 8 of the amended module).
|
||||
<5> Where Pkl found the offending value (line 3 of the input module).
|
||||
<6> What Pkl evaluated to discover the error.
|
||||
|
||||
When Pkl prints source locations, it also prints clickable links for easy access.
|
||||
For local files, it generates a link for your development environment (https://pkl-lang.org/main/current/pkl-cli/index.html#settings-file[configurable in `+~/.pkl/settings.pkl+`]).
|
||||
For packages imported from elsewhere, if available, Pkl produces `https://` links to their repository.
|
||||
|
||||
Pkl complains about a _type constraint_.
|
||||
Pkl's type system doesn't just protect you from providing a `String` where you expected an `Int`, it even checks which _values_ are allowed.
|
||||
In this case, the minimum time-out is _three_ minutes.
|
||||
If you change the value to `3`, Pkl accepts your configuration.
|
||||
|
||||
[source, shell]
|
||||
----
|
||||
$ pkl eval cicd.pkl
|
||||
timeout: 3
|
||||
pipelines: []
|
||||
----
|
||||
|
||||
You can now define a pipeline.
|
||||
Start off by specifying the name of the pipeline and nothing else.
|
||||
|
||||
[source,{pkl}]
|
||||
.cicd.pkl
|
||||
----
|
||||
amends "acmecicd.pkl"
|
||||
|
||||
timeout = 3
|
||||
pipelines {
|
||||
new { // <1>
|
||||
name = "prb"
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> There is no pipeline object to amend. The `new` keyword gives you an object to amend.
|
||||
|
||||
So far, you've defined objects the same way you amended them.
|
||||
When the name `foo` didn't occur before, `foo { ... }` _creates_ a property called `foo` and assigns to it the object specified on the `...`.
|
||||
If `foo` is an existing object, this notation is an _amend expression_; resulting in a new _object_ (value), but _not_ a new (named) property.
|
||||
Since `pipelines` is a listing, you can _add_ elements by writing expressions in an amend expression.
|
||||
|
||||
In this case, though, there is no object to amend. Writing `myNewPipeline { ... }` defines a _property_, but listings may only include _elements_.
|
||||
This is where you can use the keyword `new`.
|
||||
|
||||
`new` gives you an object to amend.
|
||||
Pkl derives from the context in which `new` is used and what the object to amend should look like.
|
||||
This is called the _default value_ for the context.
|
||||
xref:03_writing_a_template.adoc[The next part] goes into detail about how Pkl does this.
|
||||
|
||||
Running Pkl on your new configuration produces a verbose error.
|
||||
|
||||
[source,plain]
|
||||
.cicd.pkl
|
||||
----
|
||||
–– Pkl Error ––
|
||||
Pipelines that set a 'name' must also set a 'branchName'.
|
||||
|
||||
8 | throw("Pipelines that set a 'name' must also set a 'branchName'.")
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at acmecicd#Pipeline.nameRequiresBranchName.<function#1> (file:///Users/me/tutorial/acmecicd.pkl, line 8)
|
||||
|
||||
6 | name = "prb"
|
||||
^^^^^
|
||||
at cicd#pipelines[#1].name (file:///Users/me/tutorial/cicd.pkl, line 6)
|
||||
|
||||
90 | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (https://github.com/apple/pkl/blob/e4d8c882d/stdlib/base.pkl#L90)
|
||||
|
||||
----
|
||||
|
||||
You have hit another type constraint, like `timeout: Int(this >= 3)` before.
|
||||
In this case, the error message consists of an English language sentence, instead of Pkl code.
|
||||
When constraints are complicated or very application specific, template authors can `throw` a more descriptive error message like this.
|
||||
|
||||
The message is quite instructive, so you can fix the error by adding a `branchName`.
|
||||
|
||||
[source,{pkl}]
|
||||
.cicd.pkl
|
||||
----
|
||||
amends "acmecicd.pkl"
|
||||
|
||||
timeout = 3
|
||||
pipelines {
|
||||
new {
|
||||
name = "prb"
|
||||
branchName = "main"
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
and indeed
|
||||
|
||||
[source,{pkl}]
|
||||
----
|
||||
$ pkl eval -f yml /Users/me/tutorial/cicd.pkl
|
||||
timeout: 3
|
||||
pipelines:
|
||||
- name: prb
|
||||
branchName: main
|
||||
----
|
||||
441
docs/modules/language-tutorial/pages/03_writing_a_template.adoc
Normal file
441
docs/modules/language-tutorial/pages/03_writing_a_template.adoc
Normal file
@@ -0,0 +1,441 @@
|
||||
= Writing a Template
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
|
||||
In parts xref:01_basic_config.adoc[one] and xref:02_filling_out_a_template.adoc[two], you saw that Pkl provides _validation_ of our configurations.
|
||||
It checks syntax, types and constraints.
|
||||
As you saw in the `acmecicd` example xref:02_filling_out_a_template.adoc#amending-templates[here], the template can provide informative error messages when an amending module violates a type constraint.
|
||||
|
||||
In this final part, you will see some of Pkl's techniques that are particularly relevant for writing a template.
|
||||
|
||||
== Basic types
|
||||
|
||||
Pkl always checks the _syntax_ of its input.
|
||||
As it evaluates your configuration, it also checks _types_.
|
||||
You've seen objects, listings, and mappings already.
|
||||
These provide ways to write structured configuration.
|
||||
Before you can write types for them, you need to know how to write the types for the simplest (unstructured) values.
|
||||
|
||||
These are all Pkl's _basic_ types:
|
||||
|
||||
[source,{pkl}]
|
||||
.pklTutorialPart3.pkl
|
||||
----
|
||||
name: String = "Writing a Template"
|
||||
|
||||
part: Int = 3
|
||||
|
||||
hasExercises: Boolean = true
|
||||
|
||||
amountLearned: Float = 13.37
|
||||
|
||||
duration: Duration = 30.min
|
||||
|
||||
bandwidthRequirementPerSecond: DataSize = 50.mb
|
||||
----
|
||||
|
||||
In the above, you've explicitly annotated the code with type signatures.
|
||||
The default output of Pkl is actually `pcf`, which is a subset of Pkl.
|
||||
Since `pcf` does not have type signatures, running Pkl on this example removes them.
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
$ pkl eval pklTutorialPart3.pkl
|
||||
name = "Writing a Template"
|
||||
part = 3
|
||||
hasExercises = true
|
||||
amountLearned = 13.37
|
||||
duration = 30.min
|
||||
bandwidthRequirementPerSecond = 50.mb
|
||||
----
|
||||
|
||||
Note how `Duration` and `DataSize` help you prevent https://en.wikipedia.org/wiki/Mars_Climate_Orbiter[unit errors] in these common (for configuration) domains.
|
||||
|
||||
== Typed objects, properties and amending
|
||||
|
||||
Having a notation for basic types, you can now write _typed objects_.
|
||||
|
||||
[source,{pkl}]
|
||||
.simpleClass.pkl
|
||||
----
|
||||
class Language { // <1>
|
||||
name: String
|
||||
}
|
||||
|
||||
bestForConfig: Language = new { // <2>
|
||||
name = "Pkl"
|
||||
}
|
||||
----
|
||||
<1> A class definition.
|
||||
<2> A property definition, using the `Language` class.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Although not required (or enforced), it's customary to name properties starting with a lower-case letter. Class names, by that same convention, start with an upper-case letter.
|
||||
====
|
||||
|
||||
You can type objects with _classes_.
|
||||
In this example, you define a class called `Language`.
|
||||
You can now be certain that every instance of `Language` has a property `name` with type `String`.
|
||||
|
||||
Types and values are different things in Pkl.
|
||||
Pkl does not render types in its output,footnote:[Although, some output formats can contain their own form of type annotation. This may be derived from the Pkl type. Type definitions (`class` and `typealias`) themselves are never rendered.] so when you run Pkl on this, you don't see the class _definition_ at all.
|
||||
|
||||
[source,{pkl}]
|
||||
----
|
||||
$ pkl eval simpleClass.pkl
|
||||
bestForConfig {
|
||||
name = "Pkl"
|
||||
}
|
||||
|
||||
----
|
||||
|
||||
Did you notice that the output doesn't just omit the type signature, but also the `= new`?
|
||||
We will discuss this further in the next section.
|
||||
|
||||
When your configuration describes a few different parts like this, you can define one instance and amend it for every other instance.
|
||||
|
||||
For example:
|
||||
|
||||
[source,{pkl}]
|
||||
.pklTutorialParts.pkl
|
||||
----
|
||||
class TutorialPart {
|
||||
name: String
|
||||
|
||||
part: Int
|
||||
|
||||
hasExercises: Boolean
|
||||
|
||||
amountLearned: Float
|
||||
|
||||
duration: Duration
|
||||
|
||||
bandwidthRequirementPerSecond: DataSize
|
||||
}
|
||||
|
||||
pklTutorialPart1: TutorialPart = new {
|
||||
name = "Basic Configuration"
|
||||
part = 1
|
||||
hasExercises = true
|
||||
amountLearned = 13.37
|
||||
duration = 30.min
|
||||
bandwidthRequirementPerSecond = 50.mib.toUnit("mb")
|
||||
}
|
||||
|
||||
pklTutorialPart2: TutorialPart = (pklTutorialPart1) {
|
||||
name = "Filling out a Template"
|
||||
part = 2
|
||||
}
|
||||
|
||||
pklTutorialPart3: TutorialPart = (pklTutorialPart1) {
|
||||
name = "Writing a Template"
|
||||
part = 3
|
||||
}
|
||||
|
||||
----
|
||||
|
||||
You can read this as saying "``pklTutorialPart2`` & `pklTutorialPart3` are exactly like `pklTutorialPart1`, except for their `name` and `part`."
|
||||
Running Pkl confirms this:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
$ pkl eval pklTutorialParts.pkl
|
||||
pklTutorialPart1 {
|
||||
name = "Basic Configuration"
|
||||
part = 1
|
||||
hasExercises = true
|
||||
amountLearned = 13.37
|
||||
duration = 30.min
|
||||
bandwidthRequirementPerSecond = 50.mb
|
||||
}
|
||||
pklTutorialPart2 {
|
||||
name = "Filling out a Template"
|
||||
part = 2
|
||||
hasExercises = true
|
||||
amountLearned = 13.37
|
||||
duration = 30.min
|
||||
bandwidthRequirementPerSecond = 50.mb
|
||||
}
|
||||
pklTutorialPart3 {
|
||||
name = "Writing a Template"
|
||||
part = 3
|
||||
hasExercises = true
|
||||
amountLearned = 13.37
|
||||
duration = 30.min
|
||||
bandwidthRequirementPerSecond = 50.mb
|
||||
}
|
||||
|
||||
----
|
||||
|
||||
Sadly, `pklTutorialParts.pkl` is a _rewrite_ of `pklTutorial.pkl`.
|
||||
It creates a separate `class TutorialPart` and instantiates three properties with it (`pklTutorialPart1`, `pklTutorialPart2` and `pklTutorialPart3`).
|
||||
In doing so, it implicitly moves everything "down" one level (`pklTutorialPart3` is now a property in the module `pklTutorialParts`, whereas above, in `pklTutorialPart3.pkl` it was its own module).
|
||||
This is not very DRY.
|
||||
As a matter of fact, you don't need this rewrite.
|
||||
|
||||
Any `.pkl` file defines a _module_ in Pkl.
|
||||
Any module is represented by a _module class_, which is an actual Pkl `class`.
|
||||
A module is not quite the same as any other class, because Pkl never renders class definitions on the output.
|
||||
However, when you ran Pkl on `pklTutorialPart3.pkl`, it _did_ produce an output.
|
||||
This is because a module also defines an _instance_ of the module class.
|
||||
|
||||
The values given to properties in a module (or in any "normal" class) are called _default values_.
|
||||
When you instantiate a class, all the properties for which you _don't_ provide a value are populated from the class' default values.
|
||||
|
||||
In our examples of tutorial parts, only the `name` and `part` varied across instances.
|
||||
You can express this by adding default values to the (module) class definition.
|
||||
Instead of starting from a particular tutorial part, you can define the module `tutorialPart` as follows:
|
||||
|
||||
[source,{pkl}]
|
||||
.TutorialPart.pkl
|
||||
----
|
||||
name: String // <1>
|
||||
|
||||
part: Int // <1>
|
||||
|
||||
hasExercises: Boolean = true // <2>
|
||||
|
||||
amountLearned: Float = 13.37 // <2>
|
||||
|
||||
duration: Duration = 30.min // <2>
|
||||
|
||||
bandwidthRequirementPerSecond: DataSize = 50.mb // <2>
|
||||
----
|
||||
<1> No default value given.
|
||||
<2> Default value given.
|
||||
|
||||
Running this through Pkl gives an error, or course, because of the missing values:
|
||||
|
||||
[source, shell]
|
||||
----
|
||||
$ pkl eval TutorialPart.pkl
|
||||
–– Pkl Error ––
|
||||
Tried to read property `name` but its value is undefined.
|
||||
|
||||
1 | name: String
|
||||
^^^^
|
||||
...
|
||||
----
|
||||
|
||||
An individual part now only has to fill in the missing fields, so you can change `pklTutorialPart3.pkl` to amend this:
|
||||
|
||||
[source,{pkl}]
|
||||
.pklTutorialPart3.pkl
|
||||
----
|
||||
amends "TutorialPart.pkl"
|
||||
|
||||
name = "Writing a Template"
|
||||
|
||||
part = 3
|
||||
----
|
||||
|
||||
This results in
|
||||
|
||||
[source, shell]
|
||||
----
|
||||
$ pkl eval pklTutorialPart3.pkl
|
||||
name = "Writing a Template"
|
||||
part = 3
|
||||
hasExercises = true
|
||||
amountLearned = 13.37
|
||||
duration = 30.min
|
||||
bandwidthRequirementPerSecond = 50.mb
|
||||
|
||||
----
|
||||
|
||||
This now behaves exactly like our `pklTutorialPart3: TutorialPart = (pklTutorialPart1) {...` before.
|
||||
`pklTutorialPart3` is now defined as the value we get by amending `tutorialPart` and giving it a `name` and a `part`.
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
Amending anything _never changes its type_.
|
||||
When we amend an object of type `Foo`, the result will always be precisely of type `Foo`.
|
||||
By "precisely" we mean, that amending an object also can't "turn it into" an instance of a sub-class of the class of the object being amended.
|
||||
====
|
||||
|
||||
== A new template
|
||||
|
||||
Now that you know about types, you can start writing your first template.
|
||||
So far, you've written configurations with Pkl, either without a template, or using a template on Pkl Hub.
|
||||
It is often easiest to first write a (typical) configuration for which you want to create a template.
|
||||
Suppose you want to define what a live workshop for this tutorial looks like.
|
||||
Consider this example:
|
||||
|
||||
[source,{pkl}]
|
||||
.workshop2023.pkl
|
||||
----
|
||||
title = "Pkl: Configure your Systems in New Ways"
|
||||
interactive = true
|
||||
seats = 100
|
||||
occupancy = 0.85
|
||||
duration = 1.5.h
|
||||
`abstract` = """
|
||||
With more systems to configure, the software industry is drowning in repetitive and brittle configuration files.
|
||||
YAML and other configuration formats have been turned into programming languages against their will.
|
||||
Unsurprisingly, they don’t live up to the task.
|
||||
Pkl puts you back in control.
|
||||
"""
|
||||
|
||||
event {
|
||||
name = "Migrating Birds between hemispheres"
|
||||
year = 2023
|
||||
}
|
||||
|
||||
instructors {
|
||||
"Kate Sparrow"
|
||||
"Jerome Owl"
|
||||
}
|
||||
|
||||
sessions {
|
||||
new {
|
||||
date = "8/14/2023"
|
||||
time = 30.min
|
||||
}
|
||||
new {
|
||||
date = "8/15/2023"
|
||||
time = 30.min
|
||||
}
|
||||
}
|
||||
|
||||
assistants {
|
||||
["kevin"] = "Kevin Parrot"
|
||||
["betty"] = "Betty Harrier"
|
||||
}
|
||||
|
||||
agenda {
|
||||
["beginners"] {
|
||||
title = "Basic Configuration"
|
||||
part = 1
|
||||
duration = 45.min
|
||||
}
|
||||
["intermediates"] {
|
||||
title = "Filling out a Template"
|
||||
part = 2
|
||||
duration = 45.min
|
||||
}
|
||||
["experts"] {
|
||||
title = "Writing a Template"
|
||||
part = 3
|
||||
duration = 45.min
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Call your new template `Workshop.pkl`.
|
||||
Although not required, it's good practice to always name your template with a `module`-clause.
|
||||
Defining the first few properties are like you saw in the previous section:
|
||||
|
||||
[source,{pkl}]
|
||||
----
|
||||
module Workshop
|
||||
|
||||
title: String
|
||||
|
||||
interactive: Boolean
|
||||
|
||||
seats: Int
|
||||
|
||||
occupancy: Float
|
||||
|
||||
duration: Duration
|
||||
|
||||
`abstract`: String
|
||||
----
|
||||
|
||||
Unlike these first few properties, `event` is an object with multiple properties.
|
||||
To be able to type `event`, you need a `class`.
|
||||
You've seen before how to define this:
|
||||
|
||||
[source,{pkl}]
|
||||
----
|
||||
class Event {
|
||||
name: String
|
||||
|
||||
year: Int
|
||||
}
|
||||
|
||||
event: Event
|
||||
----
|
||||
|
||||
Next, `instructors` isn't an object with properties, but a list of unnamed values.
|
||||
Pkl offers the `Listing` type for this:
|
||||
|
||||
|
||||
[source,{pkl}]
|
||||
----
|
||||
instructors: Listing<String>
|
||||
----
|
||||
|
||||
`sessions` is a `Listing` of objects, so you need a `Session` class.
|
||||
[source,{pkl}]
|
||||
----
|
||||
class Session {
|
||||
time: Duration
|
||||
|
||||
date: String
|
||||
}
|
||||
|
||||
sessions: Listing<Session>
|
||||
----
|
||||
|
||||
`assistants` has a structure like an object, in that all the values are named, but the set of names is not fixed for all possible workshops (and some workshops may have more assistants than others). The Pkl type for this is a `Mapping`:
|
||||
|
||||
[source,{pkl}]
|
||||
----
|
||||
assistants: Mapping<String, String>
|
||||
----
|
||||
|
||||
Finally, for every workshop session, there is an `agenda`, which describes which ``TutorialPart``s are covered.
|
||||
You already defined `TutorialPart.pkl` as its own module, so you should not define a separate class, but rather `import` that module and reuse it here:
|
||||
|
||||
[source,{pkl}]
|
||||
----
|
||||
import "TutorialPart.pkl" // <1>
|
||||
|
||||
agenda: Mapping<String, TutorialPart>
|
||||
----
|
||||
<1> This `import` clause brings the name `TutorialPart` into scope, which is the module class as discussed above. Note that import clauses must appear before property definitions.
|
||||
|
||||
Putting it all together, your `Workshop.pkl` template looks like this:
|
||||
|
||||
[source,{pkl}]
|
||||
.Workshop.pkl
|
||||
----
|
||||
module Workshop
|
||||
|
||||
import "TutorialPart.pkl"
|
||||
|
||||
title: String
|
||||
|
||||
interactive: Boolean
|
||||
|
||||
seats: Int
|
||||
|
||||
occupancy: Float
|
||||
|
||||
duration: Duration
|
||||
|
||||
`abstract`: String
|
||||
|
||||
class Event {
|
||||
name: String
|
||||
|
||||
year: Int
|
||||
}
|
||||
|
||||
event: Event
|
||||
|
||||
instructors: Listing<String>
|
||||
|
||||
class Session {
|
||||
time: Duration
|
||||
|
||||
date: String
|
||||
}
|
||||
|
||||
sessions: Listing<Session>
|
||||
|
||||
assistants: Mapping<String, String>
|
||||
|
||||
agenda: Mapping<String, TutorialPart>
|
||||
----
|
||||
17
docs/modules/language-tutorial/pages/index.adoc
Normal file
17
docs/modules/language-tutorial/pages/index.adoc
Normal file
@@ -0,0 +1,17 @@
|
||||
= Tutorial
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
|
||||
Welcome to the Pkl tutorial. We will get you up and running quickly!
|
||||
If you are new to Pkl, we recommend that you follow along with the code examples.
|
||||
This tutorial describes interactions with the xref:pkl-cli:index.adoc#repl[REPL].
|
||||
For an even more interactive experience, follow along using a xref:main:ROOT:tools.adoc[supported editor].
|
||||
|
||||
For more comprehensive documentation, see xref:language-reference:index.adoc[Language Reference].
|
||||
For ready-to-go examples with full source code, see xref:ROOT:examples.adoc[].
|
||||
For API documentation, see xref:ROOT:standard-library.adoc[Standard Library].
|
||||
|
||||
Pick a tutorial by topic:
|
||||
|
||||
1. xref:01_basic_config.adoc[Basic Configuration]
|
||||
2. xref:02_filling_out_a_template.adoc[Filling out a Template]
|
||||
3. xref:03_writing_a_template.adoc[Writing a Template]
|
||||
782
docs/modules/pkl-cli/pages/index.adoc
Normal file
782
docs/modules/pkl-cli/pages/index.adoc
Normal file
@@ -0,0 +1,782 @@
|
||||
= CLI
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
:uri-homebrew: https://brew.sh
|
||||
:uri-pkl-macos-download: {github-releases}/pkl-cli-macos-{pkl-artifact-version}.bin
|
||||
:uri-pkl-linux-amd64-download: {github-releases}/pkl-cli-linux-amd64-{pkl-artifact-version}.bin
|
||||
:uri-pkl-linux-aarch64-download: {github-releases}/pkl-cli-linux-aarch64-{pkl-artifact-version}.bin
|
||||
:uri-pkl-alpine-download: {github-releases}/pkl-cli-alpine-amd64-{pkl-artifact-version}.bin
|
||||
:uri-pkl-java-download: {github-releases}/pkl-cli-java-{pkl-artifact-version}.jar
|
||||
:uri-pkl-stdlib-docs-settings: {uri-pkl-stdlib-docs}/settings/
|
||||
:uri-pkl-cli-main-sources: {uri-github-tree}/pkl-cli/src/main/kotlin/org/pkl/cli
|
||||
:uri-pkl-cli-CliEvaluatorOptions: {uri-pkl-cli-main-sources}/CliEvaluatorOptions.kt
|
||||
:uri-certificates: {uri-github-tree}/pkl-commons-cli/src/main/resources/org/pkl/commons/cli/commands
|
||||
|
||||
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]]
|
||||
== Installation
|
||||
|
||||
The CLI comes in multiple flavors:
|
||||
|
||||
* Native macOS executable for amd64 (tested on macOS 10.15)
|
||||
* Native Linux executable for amd64 (tested on Oracle Linux 8)
|
||||
* Native Linux executable for aarch64 (tested on Oracle Linux 8)
|
||||
* Native Alpine Linux executable for amd64 (cross-compiled and tested on Oracle Linux 8)
|
||||
* Java executable (tested with Java 8/11/14 on macOS and Oracle Linux)
|
||||
|
||||
On macOS and Linux, 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?
|
||||
[NOTE]
|
||||
====
|
||||
The Linux executable is dynamically linked against _glibc_ and _libstdc{plus}{plus}_,
|
||||
whereas, the Alpine Linux executable is statically linked against _musl libc_ and _libstdc{plus}{plus}_.
|
||||
====
|
||||
|
||||
The Java executable works on multiple platforms and has a smaller binary size than the native executables.
|
||||
However, it requires a Java 8 (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.
|
||||
|
||||
//TODO uncomment this after brew formula is merged and available
|
||||
// [[homebrew]]
|
||||
// === Homebrew
|
||||
//
|
||||
// Release versions can be installed with {uri-homebrew}[Homebrew].
|
||||
//
|
||||
// ifdef::is-release-version[]
|
||||
// To install Pkl, run:
|
||||
//
|
||||
// [source,shell]
|
||||
// ----
|
||||
// brew install pkl
|
||||
// ----
|
||||
//
|
||||
// To update Pkl, run:
|
||||
//
|
||||
// [source,shell]
|
||||
// ----
|
||||
// brew update
|
||||
// brew upgrade pkl # or just `brew upgrade`
|
||||
// ----
|
||||
// endif::[]
|
||||
//
|
||||
// ifndef::is-release-version[]
|
||||
// For instructions, switch to a release version of this page.
|
||||
// endif::[]
|
||||
|
||||
[[download]]
|
||||
=== Download
|
||||
|
||||
Development and release versions can be downloaded and installed manually.
|
||||
|
||||
=== macOS Executable
|
||||
|
||||
[source,shell]
|
||||
[subs="+attributes"]
|
||||
----
|
||||
curl -o pkl {uri-pkl-macos-download}
|
||||
chmod +x pkl
|
||||
./pkl --version
|
||||
----
|
||||
|
||||
This should print something similar to:
|
||||
|
||||
[source,shell]
|
||||
[subs="+attributes"]
|
||||
----
|
||||
Pkl {pkl-version} (macOS, native)
|
||||
----
|
||||
|
||||
[[linux-executable]]
|
||||
=== Linux Executable
|
||||
|
||||
The Linux executable is dynamically linked against _glibc_ and _libstdc{plus}{plus}_ for the amd64 and aarch64 architectures.
|
||||
For a statically linked executable, see <<Alpine Linux Executable>>.
|
||||
|
||||
On amd64:
|
||||
|
||||
[source,shell]
|
||||
[subs="+attributes"]
|
||||
----
|
||||
# on amd64
|
||||
curl -o pkl {uri-pkl-linux-amd64-download}
|
||||
chmod +x pkl
|
||||
./pkl --version
|
||||
----
|
||||
|
||||
On aarch64:
|
||||
|
||||
[source,shell]
|
||||
[subs="+attributes"]
|
||||
----
|
||||
curl -o pkl {uri-pkl-linux-aarch64-download}
|
||||
chmod +x pkl
|
||||
./pkl --version
|
||||
----
|
||||
|
||||
This should print something similar to:
|
||||
|
||||
[source,shell]
|
||||
[subs="+attributes"]
|
||||
----
|
||||
Pkl {pkl-version} (Linux, native)
|
||||
----
|
||||
|
||||
[[alpine-linux-executable]]
|
||||
=== Alpine Linux Executable
|
||||
|
||||
The Alpine Linux executable is statically linked against _musl libc_ and _libstdc{plus}{plus}_.
|
||||
For a dynamically linked executable, see <<Linux Executable>>.
|
||||
|
||||
[source,shell]
|
||||
[subs="+attributes"]
|
||||
----
|
||||
curl -o pkl {uri-pkl-alpine-download}
|
||||
chmod +x pkl
|
||||
./pkl --version
|
||||
----
|
||||
|
||||
This should print something similar to:
|
||||
|
||||
[source,shell]
|
||||
[subs="+attributes"]
|
||||
----
|
||||
Pkl {pkl-version} (Linux, native)
|
||||
----
|
||||
|
||||
NOTE: We currently do not support the aarch64 architecture for Alpine Linux.
|
||||
|
||||
=== Java Executable
|
||||
|
||||
[source,shell]
|
||||
[subs="+attributes"]
|
||||
----
|
||||
curl -o jpkl {uri-pkl-java-download}
|
||||
chmod +x jpkl
|
||||
./jpkl --version
|
||||
----
|
||||
|
||||
This should print something similar to:
|
||||
|
||||
[source,shell]
|
||||
[subs="+attributes"]
|
||||
----
|
||||
Pkl {pkl-version} (macOS 10.16, Java 11.0.9)
|
||||
----
|
||||
|
||||
[[usage]]
|
||||
== Usage
|
||||
|
||||
*Synopsis:* `pkl <subcommand> [<options>] [<args>]`
|
||||
|
||||
For a brief description of available options, run `pkl -h`.
|
||||
|
||||
NOTE: The Java executable is named `jpkl`.
|
||||
|
||||
[[command-eval]]
|
||||
=== `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
|
||||
|
||||
[[format]]
|
||||
.-f, --format
|
||||
[%collapsible]
|
||||
====
|
||||
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`.
|
||||
====
|
||||
|
||||
[[output-path]]
|
||||
.-o, --output-path
|
||||
[%collapsible]
|
||||
====
|
||||
Default: (none) +
|
||||
Example: "config.yaml" +
|
||||
The file path where the output file is placed.
|
||||
Relative paths are resolved against the project directory.
|
||||
|
||||
// suppress inspection "AsciiDocLinkResolve"
|
||||
This option is mutually exclusive with link:#multiple-file-output-path[`--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.
|
||||
// suppress inspection "AsciiDocLinkResolve"
|
||||
The separator can be customized using the link:#module-output-separator[`--module-output-separator`] option.
|
||||
====
|
||||
|
||||
[[module-output-separator]]
|
||||
.--module-output-separator
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `---` (as in a YAML stream) +
|
||||
The separator to use when multiple module outputs are written to the same file, or to standard output.
|
||||
====
|
||||
|
||||
[[multiple-file-output-path]]
|
||||
.-m, --multiple-file-output-path
|
||||
[%collapsible]
|
||||
====
|
||||
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.
|
||||
|
||||
// suppress inspection "AsciiDocLinkResolve"
|
||||
This option cannot be used together with any of the following:
|
||||
|
||||
* xref:output-path[`--output-path`]
|
||||
* xref:expression[`--expression`]
|
||||
|
||||
// suppress inspection "AsciiDocLinkResolve"
|
||||
This option supports the same placeholders as link:#output-path[`--output-path`].
|
||||
|
||||
Examples:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
# 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 xref:language-reference:index.adoc#multiple-file-output[Multiple File Output]
|
||||
in the language reference.
|
||||
====
|
||||
|
||||
[[expression]]
|
||||
.-x, --expression
|
||||
[%collapsible]
|
||||
====
|
||||
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
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
metadata {
|
||||
species = "Pigeon"
|
||||
}
|
||||
----
|
||||
|
||||
The following command prints `Pigeon` to the console:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
pkl -x metadata.name 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:
|
||||
|
||||
[source,pkl]
|
||||
----
|
||||
metadata {
|
||||
species = "Pigeon"
|
||||
}
|
||||
|
||||
output {
|
||||
text = metadata.name.toString()
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
This command also takes <<common-options, common options>>.
|
||||
|
||||
[[command-server]]
|
||||
=== `pkl server`
|
||||
|
||||
*Synopsys:* `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 xref:swift:ROOT:index.adoc[pkl-swift] or xref:go:ROOT:index.adoc[pkl-go].
|
||||
|
||||
[[command-test]]
|
||||
=== `pkl test`
|
||||
|
||||
*Synopsys:* `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.
|
||||
|
||||
<modules>::
|
||||
The absolute or relative URIs of the modules to test. Relative URIs are resolved against the working directory.
|
||||
|
||||
==== Options
|
||||
|
||||
[[junit-reports]]
|
||||
.--junit-reports
|
||||
[%collapsible]
|
||||
====
|
||||
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]]
|
||||
.--overwrite
|
||||
[%collapsible]
|
||||
====
|
||||
Force generation of expected examples. +
|
||||
The old expected files will be deleted if present.
|
||||
====
|
||||
|
||||
This command also takes <<common-options, common options>>.
|
||||
|
||||
[[command-repl]]
|
||||
=== `pkl repl`
|
||||
|
||||
*Synopsys:* `pkl repl [<options>]`
|
||||
|
||||
Start a REPL session.
|
||||
|
||||
This command takes <<common-options, common options>>.
|
||||
|
||||
[[command-project-package]]
|
||||
=== `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:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
# 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
|
||||
[%collapsible]
|
||||
====
|
||||
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
|
||||
[%collapsible]
|
||||
====
|
||||
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
|
||||
[%collapsible]
|
||||
====
|
||||
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
|
||||
[%collapsible]
|
||||
====
|
||||
Force generation of expected examples. +
|
||||
The old expected files will be deleted if present.
|
||||
====
|
||||
|
||||
This command also takes <<common-options,common options>>.
|
||||
|
||||
[[command-project-resolve]]
|
||||
=== `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 xref:language-reference:index.adoc#resolving-dependencies[resolving dependencies] section of the language reference.
|
||||
|
||||
Examples:
|
||||
[source,shell]
|
||||
----
|
||||
# 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,common options>>.
|
||||
|
||||
[[command-download-package]]
|
||||
=== `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,common options>>.
|
||||
|
||||
[[common-options]]
|
||||
=== Common options
|
||||
|
||||
The <<command-eval>>, <<command-test>>, <<command-repl>>, <<command-project-resolve>>, <<command-project-package>>, and <<command-download-package>> commands support the following common options:
|
||||
|
||||
include::../../pkl-cli/partials/cli-common-options.adoc[]
|
||||
|
||||
The <<command-eval>>, <<command-test>>, <<command-repl>>, and <<command-download-package>> commands also take the following options:
|
||||
|
||||
include::../../pkl-cli/partials/cli-project-options.adoc[]
|
||||
|
||||
== Evaluating Modules
|
||||
|
||||
Say we have the following module:
|
||||
|
||||
[[config.pkl]]
|
||||
.config.pkl
|
||||
[source,{pkl}]
|
||||
----
|
||||
bird {
|
||||
species = "Pigeon"
|
||||
diet = "Seeds"
|
||||
}
|
||||
parrot = (bird) {
|
||||
species = "Parrot"
|
||||
diet = "Berries"
|
||||
}
|
||||
----
|
||||
|
||||
To evaluate this module and write its output to standard output, run:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
pkl eval config.pkl
|
||||
----
|
||||
|
||||
You should see the following output:
|
||||
|
||||
[source,{pkl}]
|
||||
----
|
||||
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 xref:language-reference:index.adoc#module-output[Module Output].
|
||||
|
||||
To read a source module from standard input rather than a file, use `-` as a module name:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
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]]
|
||||
=== Batch Evaluation
|
||||
|
||||
Multiple modules can be evaluated at once:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
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:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
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.
|
||||
|
||||
[[repl]]
|
||||
== Working with the REPL
|
||||
|
||||
To start a REPL session, run `pkl repl`:
|
||||
|
||||
[source,shell]
|
||||
[subs="+attributes"]
|
||||
----
|
||||
$ pkl repl
|
||||
Welcome to Pkl {pkl-version}.
|
||||
Type an expression to have it evaluated.
|
||||
Type :help or :examples for more information.
|
||||
|
||||
pkl>
|
||||
----
|
||||
|
||||
NOTE: The Java executable is named `jpkl`.
|
||||
|
||||
=== Loading Modules
|
||||
|
||||
To load <<config.pkl,`config.pkl`>> into the REPL, run:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
pkl> :load config.pkl
|
||||
----
|
||||
|
||||
To evaluate the `bird.name` property, run:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
pkl> bird.name
|
||||
"Pigeon"
|
||||
----
|
||||
|
||||
To evaluate the entire module, force-evaluate `this`:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
pkl> :force this
|
||||
----
|
||||
|
||||
=== REPL Commands
|
||||
|
||||
Commands start with `:` and can be tab-completed:
|
||||
|
||||
[source,shell]
|
||||
[subs="+attributes,+macros"]
|
||||
----
|
||||
pkl> :{empty}kbd:[Tab]
|
||||
clear examples force help load quit reset
|
||||
pkl> :q{empty}kbd:[Tab]
|
||||
pkl> :quit{empty}kbd:[Return]
|
||||
$
|
||||
----
|
||||
|
||||
Commands can be abbreviated with any unique name prefix:
|
||||
|
||||
[source,shell]
|
||||
[subs="+attributes,+macros"]
|
||||
----
|
||||
pkl> :q{empty}kbd:[Return]
|
||||
$
|
||||
----
|
||||
|
||||
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 kbd:[Return].
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
pkl> 2 + 4
|
||||
6
|
||||
----
|
||||
|
||||
Apart from expressions, the REPL also accepts property, function, and class definitions.
|
||||
(See the xref:language-reference:index.adoc[Language Reference] to learn more about these language concepts.)
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
pkl> hello = "Hello, World!"
|
||||
pkl> hello
|
||||
"Hello, World!"
|
||||
pkl> function double(n) = 2 * n
|
||||
pkl> double(5)
|
||||
10
|
||||
pkl> class Bird { name: 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:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
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:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
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 xref:language-reference:index.adoc#module-amend[amends] the previous REPL module.
|
||||
|
||||
[[settings-file]]
|
||||
== 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
|
||||
[source%parsed,{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, kbd:[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 link:{uri-pkl-stdlib-docs-settings}[pkl.settings].
|
||||
|
||||
[[ca-certs]]
|
||||
== CA Certificates
|
||||
|
||||
When making TLS requests, Pkl comes with its own set of {uri-certificates}[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.
|
||||
124
docs/modules/pkl-cli/partials/cli-common-options.adoc
Normal file
124
docs/modules/pkl-cli/partials/cli-common-options.adoc
Normal file
@@ -0,0 +1,124 @@
|
||||
[[allowed-modules]]
|
||||
.--allowed-modules
|
||||
[%collapsible]
|
||||
====
|
||||
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.
|
||||
Both source modules and transitive modules are subject to this check.
|
||||
====
|
||||
|
||||
[[allowed-resources]]
|
||||
.--allowed-resources
|
||||
[%collapsible]
|
||||
====
|
||||
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.
|
||||
====
|
||||
|
||||
[[cache-dir]]
|
||||
.--cache-dir
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `~/.pkl/cache` +
|
||||
Example: `/path/to/module/cache/` +
|
||||
The cache directory for storing packages.
|
||||
====
|
||||
|
||||
.--no-cache
|
||||
[%collapsible]
|
||||
====
|
||||
Disable cacheing of packages.
|
||||
====
|
||||
|
||||
.-e, --env-var
|
||||
[%collapsible]
|
||||
====
|
||||
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
|
||||
[%collapsible]
|
||||
====
|
||||
Display help information.
|
||||
====
|
||||
|
||||
.--module-path
|
||||
[%collapsible]
|
||||
====
|
||||
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
|
||||
[%collapsible]
|
||||
====
|
||||
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
|
||||
[%collapsible]
|
||||
====
|
||||
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
|
||||
[%collapsible]
|
||||
====
|
||||
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
|
||||
[%collapsible]
|
||||
====
|
||||
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
|
||||
[%collapsible]
|
||||
====
|
||||
Display version information.
|
||||
====
|
||||
|
||||
.-w, --working-dir
|
||||
[%collapsible]
|
||||
====
|
||||
Base path that relative module paths passed as command-line arguments are resolved against.
|
||||
Defaults to the current working directory.
|
||||
====
|
||||
|
||||
.--ca-certificates
|
||||
[%collapsible]
|
||||
====
|
||||
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 xref:pkl-cli:index.adoc#ca-certs[CA Certificates].
|
||||
====
|
||||
26
docs/modules/pkl-cli/partials/cli-project-options.adoc
Normal file
26
docs/modules/pkl-cli/partials/cli-project-options.adoc
Normal file
@@ -0,0 +1,26 @@
|
||||
[[project-dir]]
|
||||
.--project-dir
|
||||
[%collapsible]
|
||||
====
|
||||
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]]
|
||||
.--omit-project-settings
|
||||
[%collapsible]
|
||||
====
|
||||
Disables loading evaluator settings from the PklProject file.
|
||||
====
|
||||
|
||||
[[no-project]]
|
||||
.--no-project
|
||||
[%collapsible]
|
||||
====
|
||||
Disables all behavior related to projects.
|
||||
====
|
||||
26
docs/modules/pkl-core/examples/CoreEvaluatorExample.java
Normal file
26
docs/modules/pkl-core/examples/CoreEvaluatorExample.java
Normal file
@@ -0,0 +1,26 @@
|
||||
import org.pkl.core.Evaluator;
|
||||
import org.pkl.core.ModuleSource;
|
||||
import java.util.List;
|
||||
|
||||
import org.pkl.core.PModule;
|
||||
import org.pkl.core.PObject;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
// the pkl/pkl-examples repo has a similar example
|
||||
@SuppressWarnings({"unchecked", "unused", "ConstantConditions"})
|
||||
public class CoreEvaluatorExample {
|
||||
@Test
|
||||
public void usage() {
|
||||
// tag::usage[]
|
||||
PModule module;
|
||||
try (var evaluator =
|
||||
Evaluator.preconfigured()) { // <1>
|
||||
module = evaluator.evaluate(
|
||||
ModuleSource.text("pigeon { age = 30; hobbies = List(\"swimming\", \"surfing\") }")); // <2>
|
||||
}
|
||||
var pigeon = (PObject) module.get("pigeon"); // <3>
|
||||
var className = pigeon.getClassInfo().getQualifiedName(); // <4>
|
||||
var hobbies = (List<String>) pigeon.get("hobbies"); // <5>
|
||||
// end::usage[]
|
||||
}
|
||||
}
|
||||
127
docs/modules/pkl-core/pages/index.adoc
Normal file
127
docs/modules/pkl-core/pages/index.adoc
Normal file
@@ -0,0 +1,127 @@
|
||||
= pkl-core Library
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
:uri-pkl-core-maven-module: {uri-maven-docsite}/artifact/org.pkl-lang/pkl-core
|
||||
:uri-pkl-core-main-sources: {uri-github-tree}/pkl-core/src/main/java/org/pkl/core
|
||||
:uri-pkl-core-test-sources: {uri-github-tree}/pkl-core/src/test/java/org/pkl/core
|
||||
:uri-pkl-core-Evaluator: {uri-pkl-core-main-sources}/Evaluator.java
|
||||
:uri-pkl-core-PModule: {uri-pkl-core-main-sources}/PModule.java
|
||||
:uri-pkl-core-PklException: {uri-pkl-core-main-sources}/PklException.java
|
||||
:uri-pkl-core-ValueVisitor: {uri-pkl-core-main-sources}/ValueVisitor.java
|
||||
:uri-pkl-core-JsonRenderer: {uri-pkl-core-main-sources}/JsonRenderer.java
|
||||
:uri-pkl-core-PcfRenderer: {uri-pkl-core-main-sources}/PcfRenderer.java
|
||||
:uri-pkl-core-PListRenderer: {uri-pkl-core-main-sources}/PListRenderer.java
|
||||
:uri-pkl-core-YamlRenderer: {uri-pkl-core-main-sources}/YamlRenderer.java
|
||||
:uri-pkl-core-SecurityManagers: {uri-pkl-core-main-sources}/SecurityManagers.java
|
||||
:uri-pkl-core-ModuleKeyFactories: {uri-pkl-core-main-sources}/module/ModuleKeyFactories.java
|
||||
|
||||
The _pkl-core_ library contains the Pkl parser, evaluator, REPL server, and xref:ROOT:standard-library.adoc[Standard Library].
|
||||
It is the foundation for most of Pkl's other libraries and tools.
|
||||
The library can also be used to embed Pkl in Java libraries and applications.
|
||||
|
||||
[[pkl-core-installation]]
|
||||
== Installation
|
||||
|
||||
The _pkl-core_ library is available {uri-pkl-core-maven-module}[from Maven Central].
|
||||
It requires Java 11 or higher.
|
||||
|
||||
=== Gradle
|
||||
|
||||
To use the library in a Gradle project, declare the following dependency:
|
||||
|
||||
[tabs]
|
||||
====
|
||||
Groovy::
|
||||
+
|
||||
.build.gradle
|
||||
[source,groovy,subs="+attributes"]
|
||||
----
|
||||
dependencies {
|
||||
compile "org.pkl-lang:pkl-core:{pkl-artifact-version}"
|
||||
}
|
||||
|
||||
ifndef::is-release-build[]
|
||||
repositories {
|
||||
maven { url "{uri-sonatype}" }
|
||||
}
|
||||
endif::[]
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
.build.gradle.kts
|
||||
[source,kotlin,subs="+attributes"]
|
||||
----
|
||||
dependencies {
|
||||
compile("org.pkl-lang:pkl-core:{pkl-artifact-version}")
|
||||
}
|
||||
|
||||
ifndef::is-release-build[]
|
||||
repositories {
|
||||
maven { url = uri("{uri-sonatype}") }
|
||||
}
|
||||
endif::[]
|
||||
----
|
||||
====
|
||||
|
||||
=== Maven
|
||||
|
||||
To use the library in a Maven project, declare the following dependency:
|
||||
|
||||
.pom.xml
|
||||
[source,xml,subs="+attributes"]
|
||||
----
|
||||
<project>
|
||||
<dependency>
|
||||
<groupId>org.pkl-lang</groupId>
|
||||
<artifactId>pkl-core</artifactId>
|
||||
<version>{pkl-artifact-version}</version>
|
||||
</dependency>
|
||||
ifndef::is-release-build[]
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>sonatype-s01</id>
|
||||
<name>Sonatype S01</name>
|
||||
<url>{uri-sonatype}</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
endif::[]
|
||||
</project>
|
||||
----
|
||||
|
||||
== Usage
|
||||
|
||||
{uri-pkl-core-Evaluator}[`Evaluator`] is the core evaluator that exposes multiple methods of evaluation.
|
||||
|
||||
The main evaluation method is `evaluate`, which returns a Java representation of the Pkl module object.
|
||||
If evaluation succeeds, a {uri-pkl-core-PModule}[`PModule`] object representing the fully evaluated module is returned.
|
||||
Otherwise, an {uri-pkl-core-PklException}[`PklException`] with error details is thrown.
|
||||
|
||||
Let's look at an example:
|
||||
|
||||
[[config-evaluator-core-example]]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{examplesdir}/CoreEvaluatorExample.java[tags=usage]
|
||||
----
|
||||
<1> Build an `Evaluator` with default configuration.
|
||||
The evaluator should be closed once it is no longer needed.
|
||||
In this example, this is done with a try-with-resources statement.
|
||||
Note that objects returned by the evaluator remain valid after calling `close()`.
|
||||
<2> Build a `ModuleSource` using the given text as the module's contents. Evaluate the given module source. Alternatively, it's possible to build a `ModuleSource` from a file, path, uri, and other sources.
|
||||
<3> Get the module's `"pigeon"` property, which is represented as `PObject` in Java.
|
||||
<4> Get the class name for this object. In this example, the class name is `pkl.base#Dynamic`.
|
||||
<5> Get pigeon's `"diet"` property, which is represented as `List<String>` in Java.
|
||||
|
||||
[[value-visitor]]
|
||||
Often, {uri-pkl-core-ValueVisitor}[`ValueVisitor`] is a better way to process a module.
|
||||
See {uri-pkl-core-PcfRenderer}[`PcfRenderer`], {uri-pkl-core-JsonRenderer}[`JsonRenderer`], {uri-pkl-core-YamlRenderer}[`YamlRenderer`] and {uri-pkl-core-PListRenderer}[`PListRenderer`] for examples.
|
||||
|
||||
[[security-manager-spi]]
|
||||
The (Pkl, not Java) security manager can be configured and customized using {uri-pkl-core-SecurityManagers}[`SecurityManagers`] and related classes.
|
||||
|
||||
[[module-loader-spi]]
|
||||
Module loaders can be configured and customized using {uri-pkl-core-ModuleKeyFactories}[`ModuleKeyFactories`] and related classes.
|
||||
|
||||
== Further Information
|
||||
|
||||
Refer to the Javadoc and sources published with the library, or browse the library's {uri-pkl-core-main-sources}[main] and {uri-pkl-core-test-sources}[test] sources.
|
||||
BIN
docs/modules/pkl-doc/assets/images/pkldoc-search.gif
Normal file
BIN
docs/modules/pkl-doc/assets/images/pkldoc-search.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 918 KiB |
241
docs/modules/pkl-doc/pages/index.adoc
Normal file
241
docs/modules/pkl-doc/pages/index.adoc
Normal file
@@ -0,0 +1,241 @@
|
||||
= Pkldoc
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
:uri-pkl-doc-maven: {uri-maven-docsite}/artifact/org.pkl-lang/pkl-doc
|
||||
:uri-DocsiteInfo: {uri-pkl-stdlib-docs}/DocsiteInfo/
|
||||
:uri-DocPackageInfo: {uri-pkl-stdlib-docs}/DocPackageInfo/
|
||||
:uri-CliDocGenerator: {uri-pkl-doc-main-sources}/CliDocGenerator.kt
|
||||
:uri-DocGenerator: {uri-pkl-doc-main-sources}/DocGenerator.kt
|
||||
|
||||
_Pkldoc_ is a documentation website generator that produces navigable and searchable API documentation for Pkl modules.
|
||||
|
||||
Pkldoc's look and feel is inspired by Scaladoc.
|
||||
To get a first impression, browse the link:{uri-pkl-stdlib-docs-index}[Standard Library API Docs].
|
||||
|
||||
== Features
|
||||
|
||||
Pkldoc offers the following features:
|
||||
|
||||
Code navigation::
|
||||
Easily navigate between hyperlinked modules, classes, functions, and properties.
|
||||
Member search::
|
||||
Search the entire documentation by member name.
|
||||
See the next section for details.
|
||||
Comment folding::
|
||||
Expand and collapse multi-paragraph doc comments.
|
||||
Markdown support::
|
||||
Write doc comments in Markdown.
|
||||
See xref:language-reference:index.adoc#doc-comments[Doc Comments] for details.
|
||||
Member links::
|
||||
Link to other members from your doc comments.
|
||||
See xref:language-reference:index.adoc#member-links[Member Links] for details.
|
||||
Member anchors::
|
||||
Get a member's deep link by clicking its anchor symbol and copying the URL in the address bar.
|
||||
Cross-site links::
|
||||
Enable cross-site member links simply by providing the URLs of other Pkldoc websites such as the standard library docs.
|
||||
|
||||
[[member-search]]
|
||||
=== Member Search
|
||||
|
||||
To get a first impression of Pkldoc's member search, let's try and find property `MinFiniteFloat` in the link:{uri-pkl-stdlib-docs-index}[standard library docs]:
|
||||
|
||||
image::pkldoc-search.gif[title="Searching the standard library docs."]
|
||||
|
||||
To start a search, press kbd:[s]. Search results are displayed as you type.
|
||||
|
||||
To limit the search to a particular kind of member, prefix the search term with _m:_ for modules, _c:_ for classes, _f:_ for functions, or _p:_ for properties.
|
||||
For example, search term _p:min_ finds property `MinFiniteFloat` but not function `min()`.
|
||||
|
||||
Camel case matching is always enabled and does not require capitalizing the search term.
|
||||
For example, search term _mff_ matches properties `MinFiniteFloat` and `MaxFiniteFloat`.
|
||||
|
||||
Both search terms and member names may contain non-ASCII Unicode characters.
|
||||
As characters are normalized to their base form, search term _res_ matches `Réseau`.
|
||||
|
||||
The `@AlsoKnownAs` annotation, defined and used throughout the _pkl.base_ module, documents alternative names for a member used in other programming languages or earlier versions of a module.
|
||||
Pkldoc's search takes these alternative names into account.
|
||||
For example, searching the standard library docs for _count_ or _size_ finds property `String.length`.
|
||||
Feel free to use `@AlsoKnownAs` in your own modules.
|
||||
|
||||
Search results are categorized into _exact_ and _other_ (partial) matches.
|
||||
On module and class pages, additional categories show matches in the same module and class.
|
||||
Within a category, results are ranked by similarity with the search term.
|
||||
|
||||
To navigate to a search result, either click the result or select it with the up/down arrow keys and press kbd:[Enter].
|
||||
|
||||
== Installation
|
||||
|
||||
Pkldoc is offered as Gradle plugin, Java library, and CLI.
|
||||
|
||||
=== Gradle Plugin
|
||||
|
||||
See xref:pkl-gradle:index.adoc#installation[Installation] in the _Gradle Plugin_ chapter.
|
||||
|
||||
[[install-library]]
|
||||
=== Java Library
|
||||
|
||||
The `pkl-doc` library is available {uri-pkl-doc-maven}[from Maven Central].
|
||||
It requires Java 11 or higher.
|
||||
|
||||
ifndef::is-release-version[]
|
||||
NOTE: Snapshots are published to repository `{uri-sonatype}`.
|
||||
endif::[]
|
||||
|
||||
==== Gradle
|
||||
|
||||
To use the library in a Gradle project, declare the following dependency:
|
||||
|
||||
[tabs]
|
||||
====
|
||||
Groovy::
|
||||
+
|
||||
.build.gradle
|
||||
[source,groovy,subs="+attributes"]
|
||||
----
|
||||
dependencies {
|
||||
compile "org.pkl-lang:pkl-doc:{pkl-artifact-version}"
|
||||
}
|
||||
|
||||
ifndef::is-release-build[]
|
||||
repositories {
|
||||
maven { url "{uri-sonatype}" }
|
||||
}
|
||||
endif::[]
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
.build.gradle.kts
|
||||
[source,kotlin,subs="+attributes"]
|
||||
----
|
||||
dependencies {
|
||||
compile("org.pkl-lang:pkl-doc:{pkl-artifact-version}")
|
||||
}
|
||||
|
||||
ifndef::is-release-build[]
|
||||
repositories {
|
||||
maven { url = uri("{uri-sonatype}") }
|
||||
}
|
||||
endif::[]
|
||||
----
|
||||
====
|
||||
|
||||
==== Maven
|
||||
|
||||
To use the library in a Maven project, declare the following dependency:
|
||||
|
||||
.pom.xml
|
||||
[source,xml,subs="+attributes"]
|
||||
----
|
||||
<project>
|
||||
<dependency>
|
||||
<groupId>org.pkl-lang</groupId>
|
||||
<artifactId>pkl-doc</artifactId>
|
||||
<version>{pkl-artifact-version}</version>
|
||||
</dependency>
|
||||
ifndef::is-release-build[]
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>sonatype-s01</id>
|
||||
<name>Sonatype S01</name>
|
||||
<url>{uri-sonatype}</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
endif::[]
|
||||
</project>
|
||||
----
|
||||
|
||||
[[install-cli]]
|
||||
=== CLI
|
||||
|
||||
The CLI is bundled with the library and does not currently ship as a native executable or a self-contained Jar.
|
||||
We recommend to provision it with a Maven compatible build tool as shown in <<install-library,Library Installation>>.
|
||||
|
||||
[[usage]]
|
||||
== Usage
|
||||
|
||||
The Pkldoc tool is offered as Gradle plugin, Java library, and CLI.
|
||||
It can generate documentation either for modules directly, or generate documentation for _package uris_.
|
||||
|
||||
The tool requires an argument of a module named `_docsite-info.pkl`, that amends link:{uri-DocsiteInfo}[pkl.DocsiteInfo].
|
||||
|
||||
[discrete]
|
||||
==== Generating documentation for modules directly
|
||||
|
||||
Modules can be passed directly to Pkldoc for documentation generation.
|
||||
When generating documentation for these modules, there must also be a module named _doc-package-info.pkl_ that amends link:{uri-DocPackageInfo}[pkl.DocPackageInfo].
|
||||
|
||||
The _doc-package-info.pkl_ module defines a _doc package_, which describes how modules are grouped and versioned together.
|
||||
|
||||
When generating documentation for modules, each such module must declare a module name that starts with a package name declared in a _doc-package-info.pkl_ module.
|
||||
For example, the following are valid module declarations for package _com.example_:
|
||||
|
||||
* `module com.example.Birds`
|
||||
* `module com.example.Birds.Parrot`
|
||||
|
||||
The part of the module name that comes after the package name
|
||||
must match the module's relative path in its source code repository.
|
||||
For example, module _com.example.Bird.Parrot_ is expected to be found at _$sourceCode/Bird/Parrot.pkl_,
|
||||
where _sourceCode_ is configured in _doc-package-info.pkl_.
|
||||
|
||||
[discrete]
|
||||
==== Generating documentation for a _package_
|
||||
|
||||
Pkldoc can alternatively generate documentation for a _package_.
|
||||
When generating documentation for a package, the URI of the package must be passed as an argument to Pkldoc.
|
||||
These packages must already be published and downloadable.
|
||||
|
||||
When generating documentation for packages, modules within a package must declare a module name that is prefixed by the package's name declared in the `Package.name` property of its `PklProject` file.
|
||||
For example, the following are valid module declarations for package `com.example`:
|
||||
|
||||
* `module com.example.Birds`
|
||||
* `module com.example.Birds.Parrot`
|
||||
|
||||
The part of the module name that comes after the package name
|
||||
must match the module's relative path in its source code repository.
|
||||
For example, module _com.example.Bird.Parrot_ is expected to be found at _$sourceCode/Bird/Parrot.pkl_,
|
||||
where _sourceCode_ is configured in the `Package.sourceCode` property of its `PklProject` file.
|
||||
|
||||
=== Gradle Plugin
|
||||
|
||||
See xref:pkl-gradle:index.adoc#pkldoc-generation[Pkldoc Generation] in the _Gradle Plugin_ chapter.
|
||||
|
||||
=== Java Library
|
||||
|
||||
The Java library offers two APIs:
|
||||
|
||||
* A high-level link:{uri-CliDocGenerator}[CliDocGenerator] API whose feature set corresponds to the CLI.
|
||||
* A low-level link:{uri-DocGenerator}[DocGenerator] API that offers additional features and control.
|
||||
|
||||
For more information, refer to the Javadoc documentation.
|
||||
|
||||
=== CLI
|
||||
|
||||
As mentioned in <<install-cli,CLI Installation>>, the CLI is bundled with the library.
|
||||
To run the CLI, execute the library Jar or its `org.pkl.doc.Main` class.
|
||||
|
||||
*Synopsis:* `java -cp <classpath> -jar pkl-doc.jar [<options>] <modules>`
|
||||
|
||||
`<modules>`::
|
||||
The absolute or relative URIs of docsite descriptors, package descriptors, and the modules for which to generate documentation.
|
||||
|
||||
Relative URIs are resolved against the working directory.
|
||||
|
||||
==== Options
|
||||
|
||||
.-o, --output-dir
|
||||
[%collapsible]
|
||||
====
|
||||
Default: (none) +
|
||||
Example: `pkldoc`
|
||||
The directory where generated documentation is placed.
|
||||
====
|
||||
|
||||
Common CLI options:
|
||||
|
||||
include::../../pkl-cli/partials/cli-common-options.adoc[]
|
||||
|
||||
[[full-example]]
|
||||
== Full Example
|
||||
|
||||
For a ready-to-go example with full source code and detailed walkthrough,
|
||||
see link:{uri-pkldoc-example}[pkldoc] in the _pkl/pkl-examples_ repository.
|
||||
693
docs/modules/pkl-gradle/pages/index.adoc
Normal file
693
docs/modules/pkl-gradle/pages/index.adoc
Normal file
@@ -0,0 +1,693 @@
|
||||
= Gradle Plugin
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
:uri-pkl-gradle-maven-module: {uri-maven-docsite}/artifact/org.pkl-lang/org.pkl-lang.gradle.plugin
|
||||
:uri-pkl-gradle-main-sources: {uri-github-tree}/pkl-gradle/src/main/java/org/pkl/gradle
|
||||
:uri-pkl-gradle-Eval: {uri-pkl-gradle-main-sources}/Eval.java
|
||||
:uri-pkl-gradle-JavaCodeGen: {uri-pkl-gradle-main-sources}/JavaCodeGen.java
|
||||
:uri-pkl-gradle-KotlinCodeGen: {uri-pkl-gradle-main-sources}/KotlinCodeGen.java
|
||||
:uri-pkl-gradle-Pkldoc: {uri-pkl-gradle-main-sources}/Pkldoc.java
|
||||
|
||||
The Gradle plugin offers the following features:
|
||||
|
||||
* <<module-evaluation,Module evaluation>>
|
||||
* <<java-code-gen,Java code generation>>
|
||||
* <<kotlin-code-gen,Kotlin code generation>>
|
||||
* <<pkldoc-generation,Pkldoc generation>>
|
||||
|
||||
Plugin versions coincide with Pkl versions.
|
||||
That is, plugin version `x.y.z` uses Pkl version `x.y.z`.
|
||||
|
||||
[[installation]]
|
||||
== Installation
|
||||
|
||||
The Gradle plugin is available {uri-pkl-gradle-maven-module}[from Maven Central].
|
||||
It requires Java 11 or higher and Gradle 6.8 or higher.
|
||||
Earlier Gradle versions are not supported.
|
||||
|
||||
ifndef::is-release-version[]
|
||||
NOTE: Snapshots are published to repository `{uri-sonatype}`.
|
||||
endif::[]
|
||||
|
||||
The plugin is applied as follows:
|
||||
|
||||
[tabs]
|
||||
====
|
||||
Groovy::
|
||||
+
|
||||
.build.gradle
|
||||
[source,groovy,subs="+attributes"]
|
||||
----
|
||||
plugins {
|
||||
id "org.pkl-lang" version "{pkl-artifact-version}"
|
||||
}
|
||||
----
|
||||
+
|
||||
.settings.gradle
|
||||
[source,groovy,subs="+attributes"]
|
||||
----
|
||||
pluginManagement {
|
||||
repositories {
|
||||
ifdef::is-release-build[]
|
||||
mavenCentral()
|
||||
endif::[]
|
||||
ifndef::is-release-build[]
|
||||
maven { url "{uri-sonatype}" }
|
||||
endif::[]
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
.build.gradle.kts
|
||||
[source,kotlin,subs="+attributes"]
|
||||
----
|
||||
plugins {
|
||||
id("org.pkl-lang") version "{pkl-artifact-version}"
|
||||
}
|
||||
----
|
||||
+
|
||||
.settings.gradle.kts
|
||||
[source,kotlin,subs="+attributes"]
|
||||
----
|
||||
pluginManagement {
|
||||
repositories {
|
||||
ifdef::is-release-build[]
|
||||
mavenCentral()
|
||||
endif::[]
|
||||
ifndef::is-release-build[]
|
||||
maven { url = uri("{uri-sonatype}") }
|
||||
endif::[]
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[module-evaluation]]
|
||||
== Module Evaluation
|
||||
|
||||
This feature integrates the xref:pkl-cli:index.adoc[Pkl evaluator] into Gradle builds.
|
||||
|
||||
=== Usage
|
||||
|
||||
To add an evaluator to the build, add a named configuration block inside `pkl.evaluators`:
|
||||
|
||||
[tabs]
|
||||
====
|
||||
build.gradle::
|
||||
+
|
||||
[source,groovy]
|
||||
----
|
||||
pkl {
|
||||
evaluators {
|
||||
evalPkl {
|
||||
sourceModules.add(file("module1.pkl"))
|
||||
transitiveModules.from file("module2.pkl")
|
||||
outputFile = layout.buildDirectory.file("module1.yaml")
|
||||
outputFormat = "yaml"
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
build.gradle.kts::
|
||||
+
|
||||
[source,kotlin]
|
||||
----
|
||||
pkl {
|
||||
evaluators {
|
||||
register("evalPkl") {
|
||||
sourceModules.add(file("module1.pkl"))
|
||||
transitiveModules.from(file("module2.pkl"))
|
||||
outputFile.set(layout.buildDirectory.file("module1.yaml"))
|
||||
outputFormat.set("yaml")
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
To guarantee correct Gradle up-to-date behavior,
|
||||
`transitiveModules` needs to contain all module files transitively referenced by `sourceModules`.
|
||||
|
||||
For each declared evaluator, the Pkl plugin creates an equally named task.
|
||||
Hence the above evaluator can be run with:
|
||||
|
||||
[source,shell script]
|
||||
----
|
||||
$ ./gradlew evalPkl
|
||||
----
|
||||
|
||||
For a ready-to-go example with full source code,
|
||||
see link:{uri-build-eval-example}[codegen-java] in the _pkl/pkl-examples_ repository.
|
||||
|
||||
=== Configuration Options
|
||||
|
||||
[[output-format]]
|
||||
.outputFormat: Property<String>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `"pcf"` +
|
||||
Example: `outputFormat = "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"`
|
||||
====
|
||||
|
||||
[[output-file]]
|
||||
.outputFile: RegularFileProperty
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `file("%\{moduleDir}/%\{moduleName}.%\{outputFormat}")` (places output files next to the source modules) +
|
||||
Example: `outputFile = layout.projectDirectory.file("config.yaml")` +
|
||||
The file path where the output file is placed.
|
||||
Relative paths are resolved against the project directory.
|
||||
|
||||
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 module 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 `outputFormat` is set.
|
||||
|
||||
If multiple sources modules are mapped to the same output file, their outputs are concatenated.
|
||||
By default, module outputs are separated with `---`, as in a YAML stream.
|
||||
// suppress inspection "AsciiDocLinkResolve"
|
||||
The separator can be customized using the link:#module-output-separator[`moduleOutputSeparator`] option.
|
||||
====
|
||||
|
||||
[[multiple-file-output-dir]]
|
||||
.multipleFileOutputDir: DirectoryProperty
|
||||
[%collapsible]
|
||||
====
|
||||
Example 1: `multipleFileOutputDir = layout.projectDirectory.dir("output")` +
|
||||
Example 2: `+multipleFileOutputDir = layout.projectDirectory.file("%{moduleDir}/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 `multipleFileOutputDir`,
|
||||
and a value determines the file's contents.
|
||||
|
||||
This option cannot be used together with any of the following:
|
||||
|
||||
* xref:output-file[outputFile]
|
||||
* xref:expression[expression]
|
||||
|
||||
This option supports the same placeholders as xref:output-file[outputFile].
|
||||
|
||||
For additional details, see xref:language-reference:index.adoc#multiple-file-output[Multiple File Output]
|
||||
in the language reference.
|
||||
====
|
||||
|
||||
[[module-output-separator]]
|
||||
.moduleOutputSeparator: Property<String>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `"---"` (as in a YAML stream) +
|
||||
The separator to use when multiple module outputs are written to the same file.
|
||||
====
|
||||
|
||||
[[expression]]
|
||||
.expression: Property<String>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: (none) +
|
||||
Example: `expression = "topLevelProperty.subValue"` +
|
||||
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 the designated output file.
|
||||
|
||||
For example, consider the following Pkl module:
|
||||
|
||||
.my-pod.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
metadata {
|
||||
name = "my-pod"
|
||||
}
|
||||
----
|
||||
|
||||
The expression `metadata.name` evaluates to text `my-pod`.
|
||||
====
|
||||
|
||||
Common properties:
|
||||
|
||||
include::../partials/gradle-modules-properties.adoc[]
|
||||
|
||||
[[tests]]
|
||||
== Tests
|
||||
|
||||
This feature integrates the xref:pkl-cli:index.adoc#usage[Pkl test evaluator] into Gradle builds.
|
||||
|
||||
=== Usage
|
||||
|
||||
To add tests to the build, add a named configuration block inside `pkl.tests`:
|
||||
|
||||
[tabs]
|
||||
====
|
||||
build.gradle::
|
||||
+
|
||||
[source,groovy]
|
||||
----
|
||||
pkl {
|
||||
tests {
|
||||
testPkl {
|
||||
sourceModules.add(files("module1_test.pkl", "module2_test.pkl"))
|
||||
junitReportsDir = layout.buildDirectory.dir("reports")
|
||||
overwrite = false
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
build.gradle.kts::
|
||||
+
|
||||
[source,kotlin]
|
||||
----
|
||||
pkl {
|
||||
tests {
|
||||
register("testPkl") {
|
||||
sourceModules.addAll(files("module1_test.pkl", "module2_test.pkl"))
|
||||
junitReportsDir.set(layout.buildDirectory.dir("reports"))
|
||||
overwrite.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[junit-reports-path]]
|
||||
.junitReportsDir: DirectoryProperty
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `null` +
|
||||
Example: `junitReportsDir = layout.buildDirectory.dir("reports")` +
|
||||
Whether and where to generate JUnit XML reports.
|
||||
====
|
||||
|
||||
[[overwrite]]
|
||||
.overwrite: Property<Boolean>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `false` +
|
||||
Whether to ignore expected example files and generate them again.
|
||||
====
|
||||
|
||||
Common properties:
|
||||
|
||||
include::../partials/gradle-modules-properties.adoc[]
|
||||
|
||||
[[java-code-gen]]
|
||||
== Java Code Generation
|
||||
|
||||
This feature integrates the xref:java-binding:codegen.adoc[Java code generator] into Gradle builds.
|
||||
|
||||
=== Usage
|
||||
|
||||
To add a Java code generator to the build, add a named configuration block inside `pkl.javaCodeGenerators`:
|
||||
|
||||
[tabs]
|
||||
====
|
||||
build.gradle::
|
||||
+
|
||||
[source,groovy]
|
||||
----
|
||||
pkl {
|
||||
javaCodeGenerators {
|
||||
genJava {
|
||||
sourceModules.addAll(files("Template1.pkl", "Template2.pkl"))
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
build.gradle.kts::
|
||||
+
|
||||
[source,kotlin]
|
||||
----
|
||||
pkl {
|
||||
javaCodeGenerators {
|
||||
register("genJava") {
|
||||
sourceModules.addAll(files("Template1.pkl", "Template2.pkl"))
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
To compile generated classes together with test code rather than main code, use `sourceSet = sourceSets.test`.
|
||||
|
||||
To generate getter methods instead of public final fields, use `generateGetters = true`.
|
||||
|
||||
For each declared Java code generator, the Pkl plugin creates an equally named task.
|
||||
Hence, the above generator can be run with:
|
||||
|
||||
[source,shell script]
|
||||
----
|
||||
$ ./gradlew genJava
|
||||
----
|
||||
|
||||
For a ready-to-go example with full source code,
|
||||
see link:{uri-codegen-java-example}[codegen-java] in the _pkl/pkl-examples_ repository.
|
||||
|
||||
=== Configuration Options
|
||||
|
||||
.generateGetters: Property<Boolean>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `false` +
|
||||
Example: `generateGetters = true` +
|
||||
Whether to generate private final fields and public getter methods rather than public final fields.
|
||||
====
|
||||
|
||||
// TODO: fixme (paramsAnnotation, nonNullAnnotation)
|
||||
.preferJavaxInjectAnnotation: Boolean
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `false` +
|
||||
Example: `preferJavaxInjectAnnotation = true` +
|
||||
Whether to annotate constructor parameters with `@javax.inject.Named` instead of `@org.pkl.config.java.mapper.Named`.
|
||||
If `true`, the generated code will have a compile dependency on `javax.inject:javax.inject:1`.
|
||||
====
|
||||
|
||||
Common code generation properties:
|
||||
|
||||
include::../partials/gradle-codegen-properties.adoc[]
|
||||
|
||||
Common properties:
|
||||
|
||||
include::../partials/gradle-modules-properties.adoc[]
|
||||
|
||||
[[kotlin-code-gen]]
|
||||
== Kotlin Code Generation
|
||||
|
||||
This feature integrates the xref:kotlin-binding:codegen.adoc[Kotlin code generator] into Gradle builds.
|
||||
|
||||
=== Usage
|
||||
|
||||
To add a Kotlin code generator to the build, add a named configuration block inside `pkl.kotlinCodeGenerators`:
|
||||
|
||||
[tabs]
|
||||
====
|
||||
build.gradle::
|
||||
+
|
||||
[source,groovy]
|
||||
----
|
||||
pkl {
|
||||
kotlinCodeGenerators {
|
||||
genKotlin {
|
||||
sourceModules.addAll(files("Template1.pkl", "Template2.pkl"))
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
build.gradle.kts::
|
||||
+
|
||||
[source,kotlin]
|
||||
----
|
||||
pkl {
|
||||
kotlinCodeGenerators {
|
||||
register("genKotlin") {
|
||||
sourceModules.addAll(files("Template1.pkl", "Template2.pkl"))
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
To compile generated classes together with test code rather than main code, use `sourceSet = sourceSets.test`.
|
||||
|
||||
For each declared Kotlin code generator, the Pkl plugin creates an equally named task. Hence the above generator can be run with:
|
||||
|
||||
[source,shell script]
|
||||
----
|
||||
$ ./gradlew genKotlin
|
||||
----
|
||||
|
||||
For a ready-to-go example with full source code,
|
||||
see link:{uri-codegen-kotlin-example}[codegen-kotlin] in the _pkl/pkl-examples_ repository.
|
||||
|
||||
=== Configuration Options
|
||||
|
||||
// TODO: fixme (generateKdoc)
|
||||
(None)
|
||||
|
||||
Common code generation properties:
|
||||
|
||||
include::../partials/gradle-codegen-properties.adoc[]
|
||||
|
||||
Common properties:
|
||||
|
||||
include::../partials/gradle-modules-properties.adoc[]
|
||||
|
||||
[[pkldoc-generation]]
|
||||
== Pkldoc generation
|
||||
|
||||
This features integrates the xref:pkl-doc:index.adoc[Pkldoc] generator into Gradle builds.
|
||||
|
||||
=== Usage
|
||||
|
||||
To add a Pkldoc generator to the build, add a named configuration block inside `pkl.pkldocGenerators`:
|
||||
|
||||
[tabs]
|
||||
====
|
||||
build.gradle::
|
||||
+
|
||||
[source,groovy]
|
||||
----
|
||||
pkl {
|
||||
pkldocGenerators {
|
||||
pkldoc {
|
||||
sourceModules.addAll(files("doc-package-info.pkl", "Template1.pkl", "Template2.pkl"))
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
build.gradle.kts::
|
||||
+
|
||||
[source,kotlin]
|
||||
----
|
||||
pkl {
|
||||
pkldocGenerators {
|
||||
register("pkldoc") {
|
||||
sourceModules.addAll(files("doc-package-info.pkl", "Template1.pkl", "Template2.pkl"))
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
For each declared Pkldoc generator, the Pkl plugin creates an equally named task.
|
||||
Hence, the above generator can be run with:
|
||||
|
||||
[source,shell script]
|
||||
----
|
||||
$ ./gradlew pkldoc
|
||||
----
|
||||
|
||||
For a ready-to-go example with full source code,
|
||||
see link:{uri-pkldoc-example}[pkldoc] in the _pkl/pkl-examples_ repository.
|
||||
|
||||
=== Configuration Options
|
||||
|
||||
The following properties can be configured inside a Pkldoc generator's configuration block:
|
||||
|
||||
.outputDir: DirectoryProperty
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `layout.buildDirectory.dir("pkldoc/<generator_name>")` +
|
||||
Example: `outputDir = layout.projectDirectory.dir("pkl-docs")` +
|
||||
The directory where generated documentation is placed.
|
||||
====
|
||||
|
||||
Common properties:
|
||||
|
||||
include::../partials/gradle-modules-properties.adoc[]
|
||||
|
||||
[[project-package]]
|
||||
== Project packaging
|
||||
This feature is the Gradle analogy for the xref:pkl-cli:index.adoc#command-project-package[project package] command in the CLI.
|
||||
It prepares package assets to be published from a project.
|
||||
|
||||
There are two differences between this feature and the CLI:
|
||||
|
||||
* Input project directories are required (the CLI determines a project from the current working directory if arguments are omitted).
|
||||
* Output directory defaults to a path within the build directory.
|
||||
|
||||
=== Usage
|
||||
|
||||
[tabs]
|
||||
====
|
||||
build.gradle::
|
||||
+
|
||||
[source,groovy]
|
||||
----
|
||||
pkl {
|
||||
project {
|
||||
packagers {
|
||||
makePackages {
|
||||
projectDirectories.from(file("pkl-config/"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
build.gradle.kts::
|
||||
+
|
||||
[source,kotlin]
|
||||
----
|
||||
pkl {
|
||||
project {
|
||||
packagers {
|
||||
register("makePackages") {
|
||||
projectDirectories.from(file("pkl-config/"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
For each declared packager, the Pkl plugin creates an equally named task.
|
||||
Hence, the above packager can be run with:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
$ ./gradlew makePackages
|
||||
----
|
||||
|
||||
=== Configuration Options
|
||||
|
||||
.projectDirectories: ConfigurableFileCollection
|
||||
[%collapsible]
|
||||
====
|
||||
Default: (none) +
|
||||
Example: `projectDirectories.from(file("pkl-config/""))` +
|
||||
The project directories to create packages for.
|
||||
====
|
||||
|
||||
.skipPublishCheck: Property<Boolean>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: (false) +
|
||||
Example: `skipPublishCheck.set(true)`
|
||||
|
||||
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.
|
||||
====
|
||||
|
||||
.outputPath: DirectoryProperty
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `project.getLayout().getBuildDirectory().dir("generated/pkl/packages")`
|
||||
|
||||
The directory to write artifacts to.
|
||||
Accepts the following placeholders:
|
||||
|
||||
`%\{name}`:: The name of the package
|
||||
`%\{version}`:: The version of the package
|
||||
====
|
||||
|
||||
.junitReportsDir: DirectoryProperty
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `null` +
|
||||
Example: `junitReportsDir = layout.buildDirectory.dir("reports")` +
|
||||
Whether and where to generate JUnit XML reports.
|
||||
====
|
||||
|
||||
.overwrite: Property<Boolean>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `false` +
|
||||
Whether to ignore expected example files and generate them again.
|
||||
====
|
||||
|
||||
Common propeties:
|
||||
|
||||
include::../partials/gradle-common-properties.adoc[]
|
||||
|
||||
== Project Resolving
|
||||
|
||||
This feature is the Gradle analogy for the xref:pkl-cli:index.adoc#command-project-resolve[project resolve] command in the CLI.
|
||||
It takes the dependencies of a project, and writes the resolved versions a file at path `PklProject.deps.json`, within the root directory of the project.
|
||||
|
||||
=== Usage
|
||||
|
||||
[tabs]
|
||||
====
|
||||
build.gradle::
|
||||
+
|
||||
[source,groovy]
|
||||
----
|
||||
pkl {
|
||||
project {
|
||||
resolvers {
|
||||
resolvePklDeps {
|
||||
projectDirectories.from(file("pkl-config/"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
build.gradle.kts::
|
||||
+
|
||||
[source,kotlin]
|
||||
----
|
||||
pkl {
|
||||
project {
|
||||
resolvers {
|
||||
register("resolvePklDeps") {
|
||||
projectDirectories.from(file("pkl-config/"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
For each declared resolver, the Pkl plugin creates an equally named task.
|
||||
Hence, the above resolver can be run with:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
$ ./gradlew resolvePklDeps
|
||||
----
|
||||
|
||||
=== Configuration Options
|
||||
|
||||
.projectDirectories: ConfigurableFileCollection
|
||||
[%collapsible]
|
||||
====
|
||||
Default: (none) +
|
||||
Example: `projectDirectories.from(file("pkl-config/""))` +
|
||||
The project directories to create packages for.
|
||||
====
|
||||
|
||||
Common propeties:
|
||||
|
||||
include::../partials/gradle-common-properties.adoc[]
|
||||
@@ -0,0 +1,39 @@
|
||||
.indent: Property<String>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `" "` (two spaces) +
|
||||
Example: `indent = "\t"` (one tab) +
|
||||
The characters to use for indenting generated source code.
|
||||
====
|
||||
|
||||
.outputDir: DirectoryProperty
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `layout.buildDirectory.dir("generated/pkl/<generator_name>")` +
|
||||
Example: `outputDir = layout.projectDirectory.dir("src/main/pkl")` +
|
||||
The directory where generated classes are placed.
|
||||
|
||||
The default places generated sources within the build directory of the project, to avoid sources from being committed into the repository on accident.
|
||||
====
|
||||
|
||||
.sourceSet: Property<SourceSet>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `sourceSets.main` (if it exists; no default otherwise) +
|
||||
Example: `sourceSet = sourceSets.test` +
|
||||
The Gradle source set that generated code is compiled together with.
|
||||
|
||||
For the codegen tasks, the `modulePath` property defaults to the compilation classpath of this source set, as well as all of the source directories of the `resource` source directory set of this source set. This setup makes it possible to rely on modules defined in classpath dependencies of your project or in the resources of your project.
|
||||
|
||||
For projects which apply the `idea` plugin and are opened in IntelliJ IDEA, this option determines whether generated sources are marked as test sources (if the source set's name contains the word "test") or regular sources (otherwise).
|
||||
====
|
||||
|
||||
.generateSpringBootConfig: Property<Boolean>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `false` +
|
||||
Example: `generateSpringBootConfig = true` +
|
||||
Whether to generate config classes for use with Spring Boot.
|
||||
====
|
||||
|
||||
// TODO: fixme (implementSerializable)
|
||||
@@ -0,0 +1,85 @@
|
||||
.allowedModules: ListProperty<String>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `["pkl:", "file:", "modulepath:", "https:", "repl:", "package:", "projectpackage:"]` +
|
||||
Example: `allowedModules = ["file:"]` +
|
||||
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.
|
||||
Both source modules and transitive modules are subject to this check.
|
||||
====
|
||||
|
||||
.allowedResources: ListProperty<String>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `["env:", "prop:", "modulepath:", "https:", "file:", "package:", "projectpackage:"]` +
|
||||
Example: `allowedResources = ["env:", "prop:"]` +
|
||||
URL patterns that determine which external resources can be read.
|
||||
Patterns are matched against the beginning of resource URLs.
|
||||
At least one pattern needs to match for a resource to be readable.
|
||||
====
|
||||
|
||||
.environmentVariables: MapProperty<String, String>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `[:]` (note that Gradle default differs from CLI default) +
|
||||
Example 1: `environmentVariables = ["MY_VAR_1": "myValue1", "MY_VAR_2": "myValue2"]` +
|
||||
Example 2: `environmentVariables = System.getenv()` +
|
||||
Environment variables that can be read by Pkl code with `read("env:<envVariableName>")`.
|
||||
====
|
||||
|
||||
.evalRootDir: DirectoryProperty
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `rootProject.layout.projectDirectory` +
|
||||
Example 1: `evalRootDir = layout.projectDirectory.dir("pkl-modules")` +
|
||||
Example 2: `evalRootDir.fileValue file("/some/absolute/path")` +
|
||||
|
||||
Root directory for `file:` modules and resources.
|
||||
If non-null, access to file-based modules and resources is restricted to those located under the root directory.
|
||||
Any symlinks are resolved before this check is performed.
|
||||
====
|
||||
|
||||
.evalTimeout: Property<java.time.Duration>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `null` +
|
||||
Example: `evalTimeout = Duration.ofSeconds(10)` +
|
||||
Duration 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.
|
||||
====
|
||||
|
||||
.externalProperties: MapProperty<String, String>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `[:]` +
|
||||
Example: `externalProperties = ["myProp1": "myValue1", "myProp2": "myValue2"]` +
|
||||
External properties that can be read by Pkl code with `read("prop:<propertyName>")`.
|
||||
====
|
||||
|
||||
.moduleCacheDir: DirectoryProperty
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `null` +
|
||||
Example 1: `moduleCacheDir = layout.buildDirectory.dir("pkl-module-cache")` +
|
||||
Example 2: `moduleCacheDir.fileValue file("/absolute/path/to/cache")` +
|
||||
The cache directory for storing packages.
|
||||
If `null`, defaults to `~/.pkl/cache`.
|
||||
====
|
||||
|
||||
.noCache: Property<Boolean>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `false` +
|
||||
Disable cacheing of packages.
|
||||
====
|
||||
|
||||
.modulePath: ConfigurableFileCollection
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `files()` (empty collection) +
|
||||
Example: `modulePath.from files("dir1", "zip1.zip", "jar1.jar")` +
|
||||
The directories, ZIP archives, or JAR archives to search when resolving `modulepath:` URIs.
|
||||
Relative paths are resolved against the project directory.
|
||||
====
|
||||
@@ -0,0 +1,69 @@
|
||||
|
||||
.sourceModules: ListProperty<Object>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `[]` +
|
||||
Example 1: `sourceModules = ["module1.pkl", "module2.pkl"]` +
|
||||
Example 2: `+sourceModules = fileTree("config").include("**/*.pkl")+` +
|
||||
List of Pkl modules which are used for this operation.
|
||||
|
||||
This property accepts the following types to represent a module:
|
||||
|
||||
* `java.net.URI`
|
||||
* `java.io.File`
|
||||
* `java.nio.file.Path`
|
||||
* `java.net.URL`
|
||||
* `java.lang.CharSequence` - if the represented string looks like a URI (it contains a scheme), the input is treated as a URI. Otherwise, it is treated as a path. Relative paths are resolved against the project directory.
|
||||
* `org.gradle.api.file.FileSystemLocation`
|
||||
====
|
||||
|
||||
.transitiveModules: ConfigurableFileCollection
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `files()` (empty collection) +
|
||||
Example 1: `transitiveModules.from files("module1.pkl", "module2.pkl")` +
|
||||
Example 2: `+transitiveModules.from fileTree("config").include("**/*.pkl")+` +
|
||||
File paths of modules that are directly or indirectly used by source modules.
|
||||
Setting this option enables correct Gradle up-to-date checks, which ensures that your Pkl tasks are executed if any of the transitive files are modified; it does not affect evaluation otherwise.
|
||||
Including source modules in `transitiveModules` is permitted but not required.
|
||||
Relative paths are resolved against the project directory.
|
||||
====
|
||||
|
||||
.projectDir: DirectoryProperty
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `null` +
|
||||
Example 1: `projectDir = layout.projectDirectory.dir("pkl")` +
|
||||
Example 2: `projectDir.fileValue file("/some/absolute/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 `null`, this is determined by searching up from the working directory for a directory that contains a `PklProject` file, until `evalRootDir` or the file system root is reached.
|
||||
====
|
||||
|
||||
.omitProjectSettings: Property<Boolean>
|
||||
[%collapsible]
|
||||
====
|
||||
Disables loading evaluator settings from the PklProject file.
|
||||
====
|
||||
|
||||
.noProject: Property<Boolean>
|
||||
[%collapsible]
|
||||
====
|
||||
Disables all behavior related to projects.
|
||||
====
|
||||
|
||||
.settingsModule: Property<Object>
|
||||
[%collapsible]
|
||||
====
|
||||
Default: `null` +
|
||||
Example: `settingsModule = layout.projectDirectory.file("mySettings.pkl")` +
|
||||
The Pkl settings module to use.
|
||||
This property accepts the same input types as the `sourceModules` property.
|
||||
|
||||
If `null`, `~/.pkl/settings.pkl` or defaults specified in the `pkl.settings` standard library module are used.
|
||||
====
|
||||
|
||||
include::../partials/gradle-common-properties.adoc[]
|
||||
8
docs/modules/release-notes/pages/0.25.adoc
Normal file
8
docs/modules/release-notes/pages/0.25.adoc
Normal file
@@ -0,0 +1,8 @@
|
||||
= Pkl 0.25 Release Notes
|
||||
:version: 0.25
|
||||
:version-minor: 0.25.0
|
||||
:release-date: <TBD>
|
||||
|
||||
This is the first release of Pkl!
|
||||
|
||||
To learn more, refer to our xref:blog:introducing-pkl.adoc[blog post].
|
||||
8
docs/modules/release-notes/pages/changelog.adoc
Normal file
8
docs/modules/release-notes/pages/changelog.adoc
Normal file
@@ -0,0 +1,8 @@
|
||||
= Changelog
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
|
||||
[[release-0.25.0]]
|
||||
== 0.25.0 (2024-02-01)
|
||||
|
||||
xref:0.25.adoc[Release notes]
|
||||
|
||||
4
docs/modules/release-notes/pages/index.adoc
Normal file
4
docs/modules/release-notes/pages/index.adoc
Normal file
@@ -0,0 +1,4 @@
|
||||
= Release Notes
|
||||
|
||||
* xref:0.25.adoc[0.25 Release Notes]
|
||||
* xref:changelog.adoc[Changelog]
|
||||
71
docs/modules/release-notes/template.adoc
Normal file
71
docs/modules/release-notes/template.adoc
Normal file
@@ -0,0 +1,71 @@
|
||||
= Pkl XXX Release Notes
|
||||
:version: XXX (e.g., 0.9)
|
||||
:version-minor: XXX (e.g., 0.9.0)
|
||||
:release-date: XXX (e.g., July 11, 2018)
|
||||
|
||||
include::ROOT:partial$component-attributes.adoc[]
|
||||
|
||||
Pkl {version} was released on {release-date}. +
|
||||
[.small]#The latest bugfix release is {version-minor}. (xref:changelog.adoc[All Versions])#
|
||||
|
||||
XXX
|
||||
|
||||
The next release (XXX) is scheduled for XXX (e.g., August 2, 2021).
|
||||
|
||||
Please send feedback and questions to https://github.com/apple/pkl/discussions[GitHub Discussions], or submit an issue on https://github.com/apple/pkl/issues/new[Github]. +
|
||||
|
||||
[small]#Pkl is hosted on https://github.com/apple/pkl[GitHub].
|
||||
To get started, follow xref:pkl-cli:index.adoc#installation[Installation].#
|
||||
|
||||
== Highlights [small]#💖#
|
||||
|
||||
News you don't want to miss.
|
||||
|
||||
.XXX
|
||||
[%collapsible]
|
||||
====
|
||||
XXX
|
||||
====
|
||||
|
||||
== Noteworthy [small]#🎶#
|
||||
|
||||
Ready when you need them.
|
||||
|
||||
.XXX
|
||||
[%collapsible]
|
||||
====
|
||||
XXX
|
||||
====
|
||||
|
||||
== Breaking Changes [small]#💔#
|
||||
|
||||
Things to watch out for when upgrading.
|
||||
|
||||
.XXX
|
||||
[%collapsible]
|
||||
====
|
||||
XXX
|
||||
====
|
||||
|
||||
== Work In Progress [small]#🚆#
|
||||
|
||||
They missed the train but deserve a mention.
|
||||
|
||||
.XXX
|
||||
[%collapsible]
|
||||
====
|
||||
XXX
|
||||
====
|
||||
|
||||
== Contributors [small]#🙏#
|
||||
|
||||
We would like to thank the contributors to this release (in alphabetical order):
|
||||
|
||||
* XXX
|
||||
|
||||
== Closed Radars [small]#🔒#
|
||||
|
||||
XXX Radars down, Inbox Zero in sight ...
|
||||
|
||||
[smaller]
|
||||
. XXX (https://github.com/apple/pkl/issues/new[XXX])
|
||||
802
docs/modules/style-guide/pages/index.adoc
Normal file
802
docs/modules/style-guide/pages/index.adoc
Normal file
@@ -0,0 +1,802 @@
|
||||
= Pkl Style Guide
|
||||
:icons: font
|
||||
:source-highlighter: highlight.js
|
||||
:pkl-expr: pkl expression
|
||||
:pkl: pkl
|
||||
:sectnums:
|
||||
|
||||
This document serves as the Pkl team's recommended coding standard for the Pkl configuration language.
|
||||
|
||||
== Files
|
||||
|
||||
=== Filename
|
||||
|
||||
Use the `.pkl` extension for all files.
|
||||
|
||||
Follow these rules for casing the file's name:
|
||||
|
||||
[cols="1,3,1"]
|
||||
|===
|
||||
| Casing | Description | Example
|
||||
| PascalCase
|
||||
| It is designed to be used as a template, or used as a class (i.e. imported and instantiated).
|
||||
| `K8sResource.pkl`
|
||||
| camelCase
|
||||
| It is designed to be used as a value.
|
||||
| `myDeployment.pkl`
|
||||
| kebab-case
|
||||
| It is designed to be used as a CLI tool.
|
||||
| `do-convert.pkl`
|
||||
|===
|
||||
|
||||
*Exception*: If a file is meant to render into a static configuration file, the filename should match the target file's name without the extension.
|
||||
For example, `config.pkl` turns into `config.yml`.
|
||||
|
||||
*Exception*: The `PklProject` file cannot have any extension.
|
||||
|
||||
=== File Encoding
|
||||
|
||||
Encode all files using UTF-8.
|
||||
|
||||
== Module Structure
|
||||
|
||||
=== Header
|
||||
|
||||
Separate each section of the module header by one blank line.
|
||||
|
||||
A module header consists of the following clauses, each of which is optional:
|
||||
|
||||
- Module clause
|
||||
- `amends` or `extends` clause
|
||||
- Import clauses
|
||||
|
||||
.module.pkl
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
module com.example.Foo // <1>
|
||||
|
||||
extends "Bar.pkl" // <2>
|
||||
|
||||
import "baz.pkl" // <3>
|
||||
import "Buz.pkl" // <3>
|
||||
----
|
||||
<1> Module clause
|
||||
<2> `extends` clause
|
||||
<3> Import clause
|
||||
|
||||
==== Module name
|
||||
|
||||
Match the name of the module with the name of the file.
|
||||
|
||||
.MyModule.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
module MyModule
|
||||
|
||||
----
|
||||
|
||||
If a module is meant to be published, add a module clause, `@ModuleInfo` annotation, and doc comments.
|
||||
|
||||
Modules that do not get published anywhere may omit a module clause.
|
||||
|
||||
.MyModule.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
/// Used for some type of purpose. <1>
|
||||
@ModuleInfo { minPklVersion = "0.24.0" } // <2>
|
||||
module MyModule // <3>
|
||||
|
||||
----
|
||||
<1> Doc comments
|
||||
<2> `@ModuleInfo` annotation
|
||||
<3> Module clause
|
||||
|
||||
==== `amends` vs. `extends` clause
|
||||
|
||||
A module that doesn't add new properties shouldn't use the `extends` clause.
|
||||
|
||||
==== Imports
|
||||
|
||||
Sort imports sections using https://en.wikipedia.org/wiki/Natural_sort_order[natural sorting] by their module URI.
|
||||
Relative path imports should be in their own section, separated by a newline.
|
||||
There should be no unused imports.
|
||||
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
import "modulepath:/foo.pkl"
|
||||
import "package://example.com/mypackage@1.0.0#/foo.pkl"
|
||||
|
||||
import ".../my/file/bar2.pkl"
|
||||
import ".../my/file/bar11.pkl"
|
||||
----
|
||||
|
||||
=== Module body
|
||||
|
||||
Within a module body, define members in this order:
|
||||
|
||||
1. Properties
|
||||
2. Methods
|
||||
3. Classes and type aliases
|
||||
4. The amended xref:language-reference:index.adoc#in-language[output] property.
|
||||
|
||||
*Exception*: local members can be close to their usage.
|
||||
|
||||
*Exception*: functions meant to be a class constructor can be next to the class declaration.
|
||||
|
||||
.constructor.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
function MyClass(_name: String): MyClass = new { name = _name }
|
||||
|
||||
class MyClass {
|
||||
name: String
|
||||
}
|
||||
----
|
||||
|
||||
=== Module URIs
|
||||
|
||||
If possible, use xref:language-reference:index.adoc#triple-dot-module-uris[triple-dot Module URIs] to reference ancestor modules
|
||||
instead of multiple `../`.
|
||||
|
||||
.good.pkl
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
amends ".../ancestor.pkl"
|
||||
|
||||
import ".../ancestor2.pkl"
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
amends "../../../ancestor.pkl"
|
||||
|
||||
import "../../../ancestor2.pkl"
|
||||
----
|
||||
|
||||
== Objects
|
||||
|
||||
=== Member spacing
|
||||
|
||||
Object members (properties, elements, and entries) should be separated by at most one blank line.
|
||||
|
||||
.good.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
foo = "bar"
|
||||
|
||||
baz = "buz"
|
||||
----
|
||||
|
||||
.good.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
foo = "bar"
|
||||
baz = "buz"
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
foo = "bar"
|
||||
|
||||
|
||||
baz = "buz"
|
||||
----
|
||||
|
||||
Too many lines separate `foo` and `baz`.
|
||||
|
||||
=== Overridden properties
|
||||
|
||||
Properties that override an existing property shouldn't have doc comments nor type annotations,
|
||||
unless the type is intentionally overridden via `extends`.
|
||||
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
amends "myOtherModule.pkl"
|
||||
|
||||
foo = "bar"
|
||||
----
|
||||
|
||||
=== New property definitions
|
||||
|
||||
Each property definition should have a type annotation and <<doc-comment,doc comment>>.
|
||||
Successive definitions should be separated by a blank line.
|
||||
|
||||
.good.pkl
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
/// Denotes something.
|
||||
myFoo: String
|
||||
|
||||
/// Something else
|
||||
myOtherFoo: String
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
/// Denotes something.
|
||||
myFoo: String
|
||||
/// Something else
|
||||
myOtherFoo: String
|
||||
----
|
||||
|
||||
=== Objects with `new`
|
||||
|
||||
When initializing a `Typed` object using `new`, omit the type.
|
||||
For example, use `new {}` instead of `new Foo {}`.
|
||||
|
||||
This rule does not apply when initializing a property to a subtype of the property's declared type.
|
||||
|
||||
.good.pkl
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
myFoo: Foo = new { foo = "bar" }
|
||||
----
|
||||
|
||||
.good.pkl
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
open class Foo {}
|
||||
class Bar extends Foo {}
|
||||
|
||||
foo: Foo = new Bar {}
|
||||
----
|
||||
|
||||
This is okay because this is meaning to initialize `Bar` instead of `Foo`.
|
||||
|
||||
.bad.pkl
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
myFoo1: Foo = new Foo { foo = "bar" } // <1>
|
||||
|
||||
myFoo2 = new Foo { foo = "bar" } // <2>
|
||||
----
|
||||
<1> Unnecessary `new Foo { ... }`
|
||||
<2> Unless amending/extendinge a module where `myFoo2` is already defined, `myFoo2` is effectively the `unknown` type, i.e. `myFoo2: unknown`.
|
||||
|
||||
== Comments
|
||||
|
||||
Use doc comments to convey information to users of a module.
|
||||
Use line comments or block comments to convey implementation concerns to authors of a module, or to comment out code.
|
||||
|
||||
[[doc-comment]]
|
||||
=== Doc comments
|
||||
|
||||
Doc comments should start with a one sentence summary paragraph, followed by additional paragraphs if necessary.
|
||||
Start new sentences on their own line.
|
||||
Add a single space after `///`.
|
||||
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
/// The time alotted for eating lunch.
|
||||
///
|
||||
/// Note:
|
||||
/// * Hamburgers typically take longer to eat than salad.
|
||||
/// * Pizza gets prepared per-order.
|
||||
///
|
||||
/// Orders must be placed on-prem.
|
||||
/// See <https://cafeteria.com> for more details.
|
||||
lunchHours: Duration
|
||||
----
|
||||
|
||||
=== Line comments
|
||||
|
||||
If a comment relates to a property definition, place it after the property's doc comments.
|
||||
Add a single space after `//`.
|
||||
|
||||
.good.pkl
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
/// Designates whether it is zebra party time.
|
||||
// TODO: Add constraints here?
|
||||
partyTime: Boolean
|
||||
----
|
||||
|
||||
A line comment may also be placed at the end of a line, as long as the line doesn't exceed 100 characters.
|
||||
|
||||
.good.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
/// Designates whether it is zebra party time.
|
||||
partyTime: Booleean // TODO: Add constraints here?
|
||||
----
|
||||
|
||||
=== Block comments
|
||||
|
||||
A single-line block comment should have a single space after `+++/*+++` and before `+++*/+++`.
|
||||
|
||||
.good.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
/* Let's have a zebra party */
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
/*Let's have a zebra party*/
|
||||
----
|
||||
|
||||
== Classes
|
||||
|
||||
=== Class names
|
||||
|
||||
Name classes in PascalCase.
|
||||
|
||||
.good.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
class ZebraParty {}
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
class zebraParty {}
|
||||
class zebraparty {}
|
||||
----
|
||||
|
||||
== Strings
|
||||
|
||||
=== Custom String Delimiters
|
||||
|
||||
Use xref:language-reference:index.adoc#custom-string-delimiters[custom string delimiters] to avoid the need for string escaping.
|
||||
|
||||
.good.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
myString = #"foo \ bar \ baz"#
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
myString = "foo \\ bar \\ baz"
|
||||
----
|
||||
|
||||
NOTE: Sometimes, using custom string delimiters makes source code harder to read. For example, the `+\#+` literal reads better using escapes (`"\\#"`) than using custom string delimimters (`+##"\#"##+`).
|
||||
|
||||
=== Interpolation
|
||||
|
||||
Prefer interpolation to string concatenation.
|
||||
|
||||
.good.pkl
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
greeting = "Hello, \(name)"
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
greeting = "Hello, " + name
|
||||
----
|
||||
|
||||
== Formatting
|
||||
|
||||
=== Line width
|
||||
|
||||
Lines shouldn't exceed 100 characters.
|
||||
|
||||
*Exceptions:*
|
||||
|
||||
1. String literals
|
||||
2. Code snippets within doc comments
|
||||
|
||||
=== Indentation
|
||||
|
||||
Use two spaces per indentation level.
|
||||
|
||||
==== Members within braces
|
||||
|
||||
Members within braces should be indented one level deeper than their parents.
|
||||
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
foo {
|
||||
bar {
|
||||
baz = "hi"
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
==== Assignment operator (`=`)
|
||||
|
||||
An assignee that starts after a newline should be indented.
|
||||
|
||||
.good.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
foo =
|
||||
"foo"
|
||||
|
||||
bar =
|
||||
new {
|
||||
baz = "baz"
|
||||
biz = "biz"
|
||||
}
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
foo =
|
||||
"foo"
|
||||
|
||||
bar =
|
||||
new {
|
||||
baz = "baz"
|
||||
biz = "biz"
|
||||
}
|
||||
----
|
||||
|
||||
An assignee that starts on the same line should not be indented.
|
||||
|
||||
.good.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
foo = new {
|
||||
baz = "baz"
|
||||
biz = "biz"
|
||||
}
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
foo = new {
|
||||
baz = "baz"
|
||||
biz = "biz"
|
||||
}
|
||||
----
|
||||
|
||||
==== `if` and `let` expressions
|
||||
|
||||
`if` and `let` bodies that start on their own line should be indented.
|
||||
Child bodies may also be inline, and the `else` branch of `if` expressions may be inline of `if`.
|
||||
|
||||
.good.pkl
|
||||
[source%parsed,{pkl-expr}]
|
||||
----
|
||||
if (bar)
|
||||
bar
|
||||
else
|
||||
foo
|
||||
----
|
||||
|
||||
.good.pkl
|
||||
[source%parsed,{pkl-expr}]
|
||||
----
|
||||
if (bar) bar else foo
|
||||
----
|
||||
|
||||
.good.pkl
|
||||
[source%parsed,{pkl-expr}]
|
||||
----
|
||||
if (bar) bar
|
||||
else foo
|
||||
----
|
||||
|
||||
.good.pkl
|
||||
[source%parsed,{pkl-expr}]
|
||||
----
|
||||
let (foo = "bar")
|
||||
foo.toUpperCase()
|
||||
----
|
||||
|
||||
.good.pkl
|
||||
[source%parsed,{pkl-expr}]
|
||||
----
|
||||
let (foo = "bar") foo.toUpperCase()
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%parsed,{pkl-expr}]
|
||||
----
|
||||
if (bar)
|
||||
bar
|
||||
else
|
||||
foo
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%parsed,{pkl-expr}]
|
||||
----
|
||||
let (foo = "bar")
|
||||
foo.toUpperCase()
|
||||
----
|
||||
|
||||
*Exception*: A nested `if` expression within the `else` branch should have the same indentation level as its parent, and start on the same line as the parent `else` keyword.
|
||||
|
||||
.good.pkl
|
||||
[source%parsed,{pkl-expr}]
|
||||
----
|
||||
if (bar)
|
||||
bar
|
||||
else if (baz)
|
||||
baz
|
||||
else
|
||||
foo
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%parsed,{pkl-expr}]
|
||||
----
|
||||
if (bar)
|
||||
bar
|
||||
else
|
||||
if (baz)
|
||||
baz
|
||||
else
|
||||
foo
|
||||
----
|
||||
|
||||
==== Multiline chained method calls
|
||||
|
||||
Indent successive multiline chained method calls.
|
||||
|
||||
[source%parsed,{pkl-expr}]
|
||||
----
|
||||
foo()
|
||||
.bar()
|
||||
.baz()
|
||||
.biz()
|
||||
----
|
||||
|
||||
==== Multiline binary operators
|
||||
|
||||
Place operators after the newline, and indent successive lines to the same level.
|
||||
|
||||
.good.pkl
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
foo = bar
|
||||
|> baz
|
||||
|> biz
|
||||
|
||||
myNum = 1
|
||||
+ 2
|
||||
+ 3
|
||||
+ 4
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
foo = bar |>
|
||||
baz |>
|
||||
biz
|
||||
|
||||
myNum = 1 +
|
||||
2 +
|
||||
3 +
|
||||
4
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
foo = bar
|
||||
|> baz
|
||||
|> biz
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
foo = bar
|
||||
|> baz
|
||||
|> biz
|
||||
----
|
||||
|
||||
*Exception*: the minus operator must come before the newline, because otherwise it is parsed as a unary minus.
|
||||
|
||||
.good.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
myNum = 1 -
|
||||
2 -
|
||||
3 -
|
||||
4
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
myNum = 1
|
||||
- 2
|
||||
- 3
|
||||
- 4
|
||||
----
|
||||
|
||||
=== Spaces
|
||||
|
||||
Add a space:
|
||||
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
amends "Foo.pkl" // <1>
|
||||
|
||||
res1 { "foo" } // <2>
|
||||
res2 = 1 + 2 // <3>
|
||||
res3 = res2 as Number // <3>
|
||||
res4 = List(1, 2, 3) // <4>
|
||||
res5 = if (foo) bar else baz // <5>
|
||||
----
|
||||
<1> After keywords
|
||||
<2> Before and after braces
|
||||
<3> Around infix operators
|
||||
<4> After a comma
|
||||
<5> Before opening parentheses in control operators (`if`, `for`, `when` are control operators)
|
||||
|
||||
NOTE: No spaces are added around the pipe symbol (`|`) in union types.
|
||||
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
typealias Foo = "foo"|"bar"|"baz"
|
||||
----
|
||||
|
||||
=== Object bodies
|
||||
|
||||
==== Single line
|
||||
|
||||
An object body may be a single line if it only consists of primitive elements, or if it contains two or fewer members.
|
||||
Otherwise, split them into multiple lines.
|
||||
|
||||
Separate each member of a single line object with a semicolon and a space.
|
||||
|
||||
.good.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
res1 = new { bar = "bar"; baz = "baz" }
|
||||
res2 = new { 1; 2; 3; 4; 5; 6 }
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%parsed,{pkl}]
|
||||
----
|
||||
res1 = new { bar = "bar"; baz = "baz"; biz = "biz"; } // <1>
|
||||
|
||||
res2 = new { 1 2 3 4 5 6 } // <2>
|
||||
----
|
||||
|
||||
<1> Too many members and trailing `;`
|
||||
<2> No semicolon
|
||||
|
||||
==== Multiline
|
||||
|
||||
Multiline objects should have their members separated by at least one line break and at most one blank line.
|
||||
|
||||
.good.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
res {
|
||||
foo = "foo"
|
||||
bar = "bar"
|
||||
}
|
||||
|
||||
res2 {
|
||||
["foo"] = "foo"
|
||||
["bar"] = "bar"
|
||||
}
|
||||
|
||||
res3 {
|
||||
"foo"
|
||||
"bar"
|
||||
}
|
||||
----
|
||||
|
||||
.good.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
res {
|
||||
foo = "foo"
|
||||
|
||||
bar = "bar"
|
||||
}
|
||||
|
||||
res2 {
|
||||
["foo"] = "foo"
|
||||
|
||||
["bar"] = "bar"
|
||||
}
|
||||
|
||||
res3 {
|
||||
"foo"
|
||||
|
||||
"bar"
|
||||
}
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
res {
|
||||
foo = "foo"
|
||||
|
||||
|
||||
bar = "bar" // <1>
|
||||
}
|
||||
|
||||
res2 {
|
||||
["foo"] = "foo"
|
||||
|
||||
|
||||
["bar"] = "bar" // <1>
|
||||
}
|
||||
|
||||
res3 {
|
||||
"foo"
|
||||
|
||||
|
||||
"bar" // <1>
|
||||
}
|
||||
|
||||
res4 {
|
||||
foo = "foo"; bar = "bar" // <2>
|
||||
}
|
||||
----
|
||||
<1> Too many blank lines between members
|
||||
<2> No line break separating members
|
||||
|
||||
Put the opening brace on the same line.
|
||||
|
||||
.good.pkl
|
||||
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
res {
|
||||
foo = "foo"
|
||||
bar = "bar"
|
||||
}
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
res
|
||||
{
|
||||
foo = "foo"
|
||||
bar = "bar"
|
||||
}
|
||||
----
|
||||
|
||||
== Programming Practices
|
||||
|
||||
=== Prefer `for` generators
|
||||
|
||||
When programmatically creating elements and entries, prefer
|
||||
xref:language-reference:index.adoc#for-generators[for generators] over using the collection API.
|
||||
Using for generators preserves xref:language-reference:index.adoc#late-binding[late binding].
|
||||
|
||||
.good.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
numbers {
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
}
|
||||
|
||||
squares {
|
||||
for (num in numbers) {
|
||||
num ** 2
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
.bad.pkl
|
||||
[source%tested,{pkl}]
|
||||
----
|
||||
numbers {
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
}
|
||||
|
||||
squares = numbers.toList().map((num) -> num ** 2).toListing()
|
||||
----
|
||||
44
docs/nav.adoc
Normal file
44
docs/nav.adoc
Normal file
@@ -0,0 +1,44 @@
|
||||
* xref:pkl-cli:index.adoc#installation[Installation]
|
||||
* xref:language-tutorial:index.adoc[Tutorial]
|
||||
** xref:language-tutorial:01_basic_config.adoc[Basic Configuration]
|
||||
** xref:language-tutorial:02_filling_out_a_template.adoc[Filling out a Template]
|
||||
** xref:language-tutorial:03_writing_a_template.adoc[Writing a Template]
|
||||
* xref:language-reference:index.adoc[Language Reference]
|
||||
|
||||
* xref:introduction:index.adoc[Introduction]
|
||||
** xref:introduction:use-cases.adoc[Use Cases]
|
||||
** xref:introduction:concepts.adoc[Concepts]
|
||||
** xref:introduction:comparison.adoc[Comparison]
|
||||
|
||||
* xref:ROOT:language.adoc[Language]
|
||||
** xref:language-tutorial:index.adoc[Tutorial]
|
||||
** xref:language-reference:index.adoc[Language Reference]
|
||||
** xref:ROOT:standard-library.adoc[Standard Library]
|
||||
|
||||
* xref:ROOT:tools.adoc[Tools]
|
||||
** xref:pkl-cli:index.adoc[CLI]
|
||||
** xref:pkl-doc:index.adoc[Pkldoc]
|
||||
** xref:pkl-gradle:index.adoc[Gradle Plugin]
|
||||
** Editor support
|
||||
*** xref:intellij:ROOT:index.adoc[IntelliJ]
|
||||
*** xref:vscode:ROOT:index.adoc[VSCode]
|
||||
*** xref:neovim:ROOT:index.adoc[Neovim]
|
||||
|
||||
* xref:ROOT:language-bindings.adoc[Language Bindings]
|
||||
** xref:java-binding:index.adoc[Java]
|
||||
*** xref:java-binding:codegen.adoc[Code Generator]
|
||||
*** xref:pkl-core:index.adoc[pkl-core Library]
|
||||
*** xref:java-binding:pkl-config-java.adoc[pkl-config-java Library]
|
||||
|
||||
** xref:kotlin-binding:index.adoc[Kotlin]
|
||||
*** xref:kotlin-binding:codegen.adoc[Code Generator]
|
||||
*** xref:kotlin-binding:pkl-config-kotlin.adoc[pkl-config-kotlin Library]
|
||||
|
||||
** xref:swift:ROOT:index.adoc[Swift]
|
||||
** xref:go:ROOT:index.adoc[Go]
|
||||
|
||||
* xref:ROOT:examples.adoc[Examples]
|
||||
|
||||
* xref:release-notes:index.adoc[Release Notes]
|
||||
** xref:release-notes:0.25.adoc[0.25 Release Notes]
|
||||
** xref:release-notes:changelog.adoc[Changelog]
|
||||
369
docs/src/test/kotlin/DocSnippetTests.kt
Normal file
369
docs/src/test/kotlin/DocSnippetTests.kt
Normal file
@@ -0,0 +1,369 @@
|
||||
import org.junit.platform.commons.annotation.Testable
|
||||
import org.junit.platform.engine.*
|
||||
import org.junit.platform.engine.TestDescriptor.Type
|
||||
import org.junit.platform.engine.discovery.ClassSelector
|
||||
import org.junit.platform.engine.discovery.MethodSelector
|
||||
import org.junit.platform.engine.discovery.PackageSelector
|
||||
import org.junit.platform.engine.discovery.UniqueIdSelector
|
||||
import org.junit.platform.engine.support.descriptor.*
|
||||
import org.junit.platform.engine.support.hierarchical.EngineExecutionContext
|
||||
import org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine
|
||||
import org.junit.platform.engine.support.hierarchical.Node
|
||||
import org.junit.platform.engine.support.hierarchical.Node.DynamicTestExecutor
|
||||
import org.opentest4j.MultipleFailuresError
|
||||
import org.pkl.commons.test.FileTestUtils.rootProjectDir
|
||||
import org.pkl.core.Loggers
|
||||
import org.pkl.core.SecurityManagers
|
||||
import org.pkl.core.StackFrameTransformers
|
||||
import org.pkl.core.module.ModuleKeyFactories
|
||||
import org.pkl.core.parser.LexParseException
|
||||
import org.pkl.core.parser.Parser
|
||||
import org.pkl.core.parser.antlr.PklParser
|
||||
import org.pkl.core.repl.ReplRequest
|
||||
import org.pkl.core.repl.ReplResponse
|
||||
import org.pkl.core.repl.ReplServer
|
||||
import org.pkl.core.resource.ResourceReaders
|
||||
import org.pkl.core.util.IoUtils
|
||||
import org.antlr.v4.runtime.ParserRuleContext
|
||||
import java.nio.file.Files
|
||||
import kotlin.io.path.isDirectory
|
||||
import kotlin.io.path.isRegularFile
|
||||
import kotlin.io.path.useDirectoryEntries
|
||||
|
||||
@Testable
|
||||
class DocSnippetTests
|
||||
|
||||
class DocSnippetTestsEngine : HierarchicalTestEngine<DocSnippetTestsEngine.ExecutionContext>() {
|
||||
private val projectDir = rootProjectDir.resolve("docs")
|
||||
private val docsDir = projectDir.resolve("modules")
|
||||
|
||||
companion object {
|
||||
val headingRegex = Regex("""(?u)^\s*(=++)\s*(.+)""")
|
||||
val collapsibleBlockRegex = Regex("""(?u)^\s*\[%collapsible""")
|
||||
val codeBlockRegex = Regex("""(?u)^\s*\[source(?:%(tested|parsed)(%error)?)?(?:,(?:\{)?([a-zA-Z-_]+)}?)?""")
|
||||
val codeBlockNameRegex = Regex("""(?u)^\s*\.(.+)""")
|
||||
val codeBlockDelimiterRegex = Regex("""(?u)^\s*----""")
|
||||
val graphicsRegex = Regex("\\[small]#.+#")
|
||||
}
|
||||
|
||||
override fun getId() = "pkl-doc-tests"
|
||||
|
||||
override fun discover(
|
||||
discoveryRequest: EngineDiscoveryRequest,
|
||||
uniqueId: UniqueId
|
||||
): TestDescriptor {
|
||||
val packageSelectors = discoveryRequest.getSelectorsByType(PackageSelector::class.java)
|
||||
val classSelectors = discoveryRequest.getSelectorsByType(ClassSelector::class.java)
|
||||
val methodSelectors = discoveryRequest.getSelectorsByType(MethodSelector::class.java)
|
||||
val uniqueIdSelectors = discoveryRequest.getSelectorsByType(UniqueIdSelector::class.java)
|
||||
|
||||
val testClass = DocSnippetTests::class.java
|
||||
val packageName = testClass.`package`.name
|
||||
val className = testClass.name
|
||||
|
||||
if (methodSelectors.isEmpty()
|
||||
&& (packageSelectors.isEmpty() || packageSelectors.any { it.packageName == packageName })
|
||||
&& (classSelectors.isEmpty() || classSelectors.any { it.className == className })
|
||||
) {
|
||||
|
||||
val rootDescriptor = Descriptor.Path(uniqueId, docsDir.fileName.toString(), ClassSource.from(testClass), docsDir)
|
||||
doDiscover(rootDescriptor, uniqueIdSelectors)
|
||||
return rootDescriptor
|
||||
}
|
||||
|
||||
// return empty descriptor w/o children
|
||||
return EngineDescriptor(uniqueId, javaClass.simpleName)
|
||||
}
|
||||
|
||||
override fun createExecutionContext(request: ExecutionRequest): ExecutionContext {
|
||||
val replServer = ReplServer(
|
||||
SecurityManagers.defaultManager,
|
||||
Loggers.stdErr(),
|
||||
listOf(
|
||||
ModuleKeyFactories.standardLibrary,
|
||||
ModuleKeyFactories.classPath(DocSnippetTests::class.java.classLoader),
|
||||
ModuleKeyFactories.file
|
||||
),
|
||||
listOf(
|
||||
ResourceReaders.environmentVariable(),
|
||||
ResourceReaders.externalProperty()
|
||||
),
|
||||
System.getenv(),
|
||||
emptyMap(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
IoUtils.getCurrentWorkingDir(),
|
||||
StackFrameTransformers.defaultTransformer
|
||||
)
|
||||
return ExecutionContext(replServer)
|
||||
}
|
||||
|
||||
private fun doDiscover(rootDescriptor: TestDescriptor, selectors: List<UniqueIdSelector>) {
|
||||
fun isMatch(other: UniqueId) = selectors.isEmpty() || selectors.any {
|
||||
it.uniqueId.hasPrefix(other) || other.hasPrefix(it.uniqueId)
|
||||
}
|
||||
|
||||
docsDir.useDirectoryEntries { docsDirEntries ->
|
||||
for (docsDirEntry in docsDirEntries) {
|
||||
if (!docsDirEntry.isDirectory()) continue
|
||||
|
||||
val docsDirEntryName = docsDirEntry.fileName.toString()
|
||||
val docsDirEntryId = rootDescriptor.uniqueId.append("dir", docsDirEntryName)
|
||||
if (!isMatch(docsDirEntryId)) continue
|
||||
|
||||
val docsDirEntryDescriptor = Descriptor.Path(
|
||||
docsDirEntryId,
|
||||
docsDirEntryName,
|
||||
DirectorySource.from(docsDirEntry.toFile()),
|
||||
docsDirEntry
|
||||
)
|
||||
rootDescriptor.addChild(docsDirEntryDescriptor)
|
||||
|
||||
val pagesDir = docsDirEntry.resolve("pages")
|
||||
if (!pagesDir.isDirectory()) continue
|
||||
|
||||
pagesDir.useDirectoryEntries { pagesDirEntries ->
|
||||
for (pagesDirEntry in pagesDirEntries) {
|
||||
val pagesDirEntryName = pagesDirEntry.fileName.toString()
|
||||
val pagesDirEntryId = docsDirEntryId.append("file", pagesDirEntryName)
|
||||
if (!pagesDirEntry.isRegularFile() ||
|
||||
!pagesDirEntryName.endsWith(".adoc") ||
|
||||
!isMatch(pagesDirEntryId)
|
||||
) continue
|
||||
|
||||
val pagesDirEntryDescriptor = Descriptor.Path(
|
||||
pagesDirEntryId,
|
||||
pagesDirEntryName,
|
||||
FileSource.from(pagesDirEntry.toFile()),
|
||||
pagesDirEntry
|
||||
)
|
||||
docsDirEntryDescriptor.addChild(pagesDirEntryDescriptor)
|
||||
|
||||
parseAsciidoc(pagesDirEntryDescriptor, selectors)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseAsciidoc(docDescriptor: Descriptor.Path, selectors: List<UniqueIdSelector>) {
|
||||
var line = ""
|
||||
var prevLine = ""
|
||||
var lineNum = 0
|
||||
var codeBlockNum = 0
|
||||
val sections = ArrayDeque<Descriptor.Section>()
|
||||
|
||||
Files.lines(docDescriptor.path).use { linesStream ->
|
||||
val linesIterator = linesStream.iterator()
|
||||
|
||||
fun advance() {
|
||||
prevLine = line
|
||||
line = linesIterator.next()
|
||||
lineNum += 1
|
||||
}
|
||||
|
||||
fun addSection(title: String, newLevel: Int) {
|
||||
while (sections.isNotEmpty() && sections.first().level >= newLevel) {
|
||||
sections.removeFirst()
|
||||
}
|
||||
|
||||
val parent = sections.firstOrNull() ?: docDescriptor
|
||||
val normalizedTitle = title
|
||||
.replace("<code>", "")
|
||||
.replace("</code>", "")
|
||||
.replace(graphicsRegex, "")
|
||||
.trim()
|
||||
val childSection = Descriptor.Section(
|
||||
parent.uniqueId.append("section", normalizedTitle),
|
||||
normalizedTitle,
|
||||
FileSource.from(docDescriptor.path.toFile(), FilePosition.from(lineNum)),
|
||||
newLevel
|
||||
)
|
||||
|
||||
sections.addFirst(childSection)
|
||||
parent.addChild(childSection)
|
||||
codeBlockNum = 0
|
||||
}
|
||||
|
||||
nextLine@ while (linesIterator.hasNext()) {
|
||||
advance()
|
||||
|
||||
val headingMatch = headingRegex.find(line)
|
||||
if (headingMatch != null) {
|
||||
val (markup, title) = headingMatch.destructured
|
||||
val newLevel = markup.length
|
||||
// ignore level 1 heading (we already have a test node for the file)
|
||||
if (newLevel == 1) continue
|
||||
addSection(title, newLevel)
|
||||
continue
|
||||
}
|
||||
|
||||
val collapsibleBlockMatch = collapsibleBlockRegex.find(line)
|
||||
if (collapsibleBlockMatch != null) {
|
||||
val blockName = codeBlockNameRegex.find(prevLine)?.groupValues?.get(1) ?: "Details"
|
||||
val newLevel = 999
|
||||
addSection(blockName, newLevel)
|
||||
continue
|
||||
}
|
||||
|
||||
val codeBlockMatch = codeBlockRegex.find(line)
|
||||
if (codeBlockMatch != null) {
|
||||
codeBlockNum += 1
|
||||
val (testMode, error, language) = codeBlockMatch.destructured
|
||||
if (testMode.isNotEmpty()) {
|
||||
val blockName = codeBlockNameRegex.find(prevLine)?.groupValues?.get(1) ?: "snippet$codeBlockNum"
|
||||
while (linesIterator.hasNext()) {
|
||||
advance()
|
||||
val startDelimiterMatch = codeBlockDelimiterRegex.find(line)
|
||||
if (startDelimiterMatch != null) {
|
||||
val jumpToLineNum = lineNum + 1
|
||||
val builder = StringBuilder()
|
||||
while (linesIterator.hasNext()) {
|
||||
advance()
|
||||
val endDelimiterMatch = codeBlockDelimiterRegex.find(line)
|
||||
if (endDelimiterMatch != null) {
|
||||
val section = sections.first()
|
||||
val snippetId = section.uniqueId.append("snippet", blockName)
|
||||
if (selectors.isEmpty() || selectors.any { snippetId.hasPrefix(it.uniqueId) }) {
|
||||
section.addChild(
|
||||
Descriptor.Snippet(
|
||||
snippetId,
|
||||
blockName,
|
||||
language,
|
||||
FileSource.from(docDescriptor.path.toFile(), FilePosition.from(jumpToLineNum)),
|
||||
builder.toString(),
|
||||
testMode == "parsed",
|
||||
error.isNotEmpty()
|
||||
)
|
||||
)
|
||||
}
|
||||
continue@nextLine
|
||||
}
|
||||
builder.appendLine(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ExecutionContext(val replServer: ReplServer) : EngineExecutionContext, AutoCloseable {
|
||||
override fun close() {
|
||||
replServer.close()
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class Descriptor(
|
||||
uniqueId: UniqueId,
|
||||
displayName: String,
|
||||
source: TestSource
|
||||
) : AbstractTestDescriptor(uniqueId, displayName, source), Node<ExecutionContext> {
|
||||
|
||||
class Path(
|
||||
uniqueId: UniqueId,
|
||||
displayName: String,
|
||||
source: TestSource,
|
||||
val path: java.nio.file.Path
|
||||
) : Descriptor(uniqueId, displayName, source) {
|
||||
|
||||
override fun getType() = Type.CONTAINER
|
||||
}
|
||||
|
||||
class Section(
|
||||
uniqueId: UniqueId,
|
||||
displayName: String,
|
||||
source: TestSource,
|
||||
val level: Int
|
||||
) : Descriptor(uniqueId, displayName, source) {
|
||||
|
||||
override fun getType() = Type.CONTAINER
|
||||
|
||||
override fun before(context: ExecutionContext): ExecutionContext {
|
||||
context.replServer.handleRequest(ReplRequest.Reset("reset"))
|
||||
return context
|
||||
}
|
||||
}
|
||||
|
||||
class Snippet(
|
||||
uniqueId: UniqueId,
|
||||
displayName: String,
|
||||
private val language: String,
|
||||
source: TestSource,
|
||||
private val code: String,
|
||||
private val parseOnly: Boolean,
|
||||
private val expectError: Boolean
|
||||
) : Descriptor(uniqueId, displayName, source) {
|
||||
|
||||
override fun getType() = Type.TEST
|
||||
|
||||
private val parsed: ParserRuleContext by lazy {
|
||||
when (language) {
|
||||
"pkl" -> Parser().parseModule(code)
|
||||
"pkl-expr" -> Parser().parseExpressionInput(code)
|
||||
else -> throw(Exception("Unrecognized language: $language"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun execute(context: ExecutionContext, executor: DynamicTestExecutor): ExecutionContext {
|
||||
if (parseOnly) {
|
||||
try {
|
||||
parsed
|
||||
if (expectError) {
|
||||
throw AssertionError("Expected a parse error, but got none.")
|
||||
}
|
||||
} catch (e: LexParseException) {
|
||||
if (!expectError) {
|
||||
throw AssertionError(e.message)
|
||||
}
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
context.replServer.handleRequest(
|
||||
ReplRequest.Eval(
|
||||
"snippet",
|
||||
code,
|
||||
!expectError,
|
||||
!expectError
|
||||
)
|
||||
)
|
||||
|
||||
val properties = parsed.children.filterIsInstance<PklParser.ClassPropertyContext>()
|
||||
|
||||
val responses = mutableListOf<ReplResponse>()
|
||||
|
||||
// force each property
|
||||
for (prop in properties) {
|
||||
responses.addAll(context.replServer.handleRequest(
|
||||
ReplRequest.Eval(
|
||||
"snippet",
|
||||
prop.Identifier().text,
|
||||
false,
|
||||
true
|
||||
)
|
||||
))
|
||||
}
|
||||
if (expectError) {
|
||||
if (responses.dropLast(1).any { it !is ReplResponse.EvalSuccess } ||
|
||||
responses.last() !is ReplResponse.EvalError) {
|
||||
throw AssertionError(
|
||||
"Expected %error snippet to fail at the end, but got the following REPL responses:\n\n" +
|
||||
responses.joinToString("\n\n") { it.message })
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
val badResponses = responses.filter { it !is ReplResponse.EvalSuccess }
|
||||
if (badResponses.isNotEmpty()) {
|
||||
throw MultipleFailuresError(null, badResponses.map { AssertionError(it.message) })
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
DocSnippetTestsEngine
|
||||
Reference in New Issue
Block a user