Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions bench/src/jmh/java/org/pkl/core/ListSort.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -53,7 +53,8 @@ public class ListSort {
IoUtils.getCurrentWorkingDir(),
StackFrameTransformers.defaultTransformer,
false,
TraceMode.COMPACT);
TraceMode.COMPACT,
Map.of());
private static final List<Object> list = new ArrayList<>(100000);

static {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,28 @@ project: Project?
/// Added in Pkl 0.26.0.
http: Http?

/// Register external module readers.
///
/// Added in Pkl 0.27.0.
externalModuleReaders: Mapping<String, ExternalReader>?

/// Register external resource readers.
///
/// Added in Pkl 0.27.0.
externalResourceReaders: Mapping<String, ExternalReader>?

/// Dictates the rendering of calls to the trace() method within Pkl.
///
/// Added in Pkl 0.30.0.
traceMode: ("compact" | "pretty")?
Comment on lines +129 to +142

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, we've missed updating this for a bit!


/// Feature flags to enable or disable.
///
/// Specifying a feature flag unknown to the current version of Pkl will result in a warning.
///
/// Added in Pkl 0.33.0.
featureFlags: Mapping<String, Boolean>?

class ClientResourceReader {
/// The URI scheme this reader is responsible for reading.
scheme: String
Expand Down Expand Up @@ -177,7 +199,7 @@ class Project {
projectFileUri: String

/// The dependencies of this project.
dependencies: Mapping<String, Project|RemoteDependency>
dependencies: Mapping<String, Project | RemoteDependency>
}

class RemoteDependency {
Expand Down Expand Up @@ -256,6 +278,23 @@ class Proxy {
/// ```
noProxy: Listing<String>(isDistinct)
}

class ExternalReader {
/// The executable to launch as the external reader process.
///
/// Must be either an absolute path to the executable or an executable name to resolve against the operating system's `$PATH`.
executable: String

/// Arguments to pass to [executable].
arguments: Listing<String>?

/// External reader process working directory.
///
/// Default value: the working directory of the Pkl process.
///
/// Added in Pkl 0.32.0.
workingDir: String?
}
----
<1> link:{uri-messagepack-bin}[bin format]

Expand Down
14 changes: 14 additions & 0 deletions docs/modules/pkl-cli/partials/cli-common-options.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,17 @@ Default: `compact` +
Specifies how `trace()` output is formatted.
Possible options are `compact` and `pretty`.
====

.--feature
[%collapsible]
====
Default: (none) +
Example: `magic=true` +

Enable or disable feature flags.

Available feature flags and their default values are provided in the full command line help.
Specifying a feature flag unknown to the current version of Pkl will result in a warning.

Providing only the name of a flag is equivalent to setting its value to `true`.
====
1 change: 1 addition & 0 deletions docs/src/test/kotlin/DocSnippetTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ class DocSnippetTestsEngine : HierarchicalTestEngine<DocSnippetTestsEngine.Execu
StackFrameTransformers.defaultTransformer,
false,
TraceMode.COMPACT,
emptyMap(),
)
return ExecutionContext(replServer)
}
Expand Down
1 change: 1 addition & 0 deletions pkl-cli/src/main/kotlin/org/pkl/cli/CliRepl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ internal class CliRepl(private val options: CliEvaluatorOptions) : CliCommand(op
stackFrameTransformer,
options.base.color?.hasColor() ?: false,
options.base.traceMode ?: TraceMode.COMPACT,
options.base.featureFlags?.asFeatureFlags("cli") ?: emptyMap(),
)
Repl(options.base.normalizedWorkingDir, server, options.base.color?.hasColor() ?: false).run()
}
Expand Down
3 changes: 2 additions & 1 deletion pkl-cli/src/test/kotlin/org/pkl/cli/repl/ReplMessagesTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -45,6 +45,7 @@ class ReplMessagesTest {
StackFrameTransformers.defaultTransformer,
false,
TraceMode.COMPACT,
emptyMap(),
)

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ data class CliBaseOptions(

/** Whether power assertions are enabled. */
val powerAssertionsEnabled: Boolean = false,

/** Feature flag state. */
val featureFlags: Map<String, Boolean>? = null,
) {

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,22 @@ abstract class CliCommand(protected val cliOptions: CliBaseOptions) {
cliOptions.traceMode ?: evaluatorSettings?.traceMode ?: TraceMode.COMPACT
}

private val featureFlags: Map<FeatureFlag, Boolean> by lazy {
cliOptions.featureFlags?.asFeatureFlags("cli")
?: evaluatorSettings?.featureFlags?.asFeatureFlags("PklProject")
?: emptyMap()
}

protected fun Map<String, Boolean>.asFeatureFlags(context: String): Map<FeatureFlag, Boolean> =
mapNotNull { entry ->
FeatureFlag.parse(entry.key)?.let {
return@mapNotNull it to entry.value
}
logger.warn("Unrecognized feature flag named `${entry.key}`", "pkl:#${context}")
return@mapNotNull null
}
.toMap()

private fun HttpClient.Builder.addDefaultCliCertificates() {
val caCertsDir = IoUtils.getPklHomeDir().resolve("cacerts")
var certsAdded = false
Expand Down Expand Up @@ -259,6 +275,8 @@ abstract class CliCommand(protected val cliOptions: CliBaseOptions) {
}
}

val logger: Logger = Loggers.stdErr()

protected fun moduleKeyFactories(modulePathResolver: ModulePathResolver): List<ModuleKeyFactory> {
return buildList {
externalModuleReaders.forEach { (key, value) ->
Expand Down Expand Up @@ -310,10 +328,11 @@ abstract class CliCommand(protected val cliOptions: CliBaseOptions) {
.addModuleKeyFactories(moduleKeyFactories(modulePathResolver))
.addResourceReaders(resourceReaders(modulePathResolver))
.setColor(useColor)
.setLogger(Loggers.stdErr())
.setLogger(logger)
.setTimeout(cliOptions.timeout)
.setModuleCacheDir(moduleCacheDir)
.setTraceMode(traceMode)
.setPowerAssertionsEnabled(cliOptions.powerAssertionsEnabled)
.setFeatureFlags(featureFlags)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package org.pkl.commons.cli.commands
import com.github.ajalt.clikt.completion.CompletionCandidates
import com.github.ajalt.clikt.parameters.groups.OptionGroup
import com.github.ajalt.clikt.parameters.options.*
import com.github.ajalt.clikt.parameters.transform.TransformContext
import com.github.ajalt.clikt.parameters.types.enum
import com.github.ajalt.clikt.parameters.types.int
import com.github.ajalt.clikt.parameters.types.long
Expand All @@ -27,10 +28,12 @@ import java.net.URI
import java.net.URISyntaxException
import java.nio.file.Path
import java.time.Duration
import java.util.Locale
import java.util.regex.Pattern
import org.pkl.commons.cli.CliBaseOptions
import org.pkl.commons.cli.CliException
import org.pkl.commons.shlex
import org.pkl.core.FeatureFlag
import org.pkl.core.evaluatorSettings.Color
import org.pkl.core.evaluatorSettings.PklEvaluatorSettings.ExternalReader
import org.pkl.core.evaluatorSettings.TraceMode
Expand Down Expand Up @@ -92,7 +95,7 @@ class BaseOptions : OptionGroup() {
> {
return splitPair(delimiter).convert {
val cmd = shlex(it.second)
Pair(it.first, ExternalReader(cmd.first(), cmd.drop(1), null))
it.first to ExternalReader(cmd.first(), cmd.drop(1), null)
}
}

Expand Down Expand Up @@ -350,6 +353,39 @@ class BaseOptions : OptionGroup() {
.enum<TraceMode> { it.name.lowercase() }
.single()

val featureFlags: Map<String, Boolean> by
option(
names = arrayOf("--feature"),
metavar = "<feature>[=<boolean>]",
help =
"Feature flag enabled state. Omitting <boolean> is equivalent to true. <${FeatureFlag.entries.joinToString(", ") { "${it.name.lowercase(Locale.ROOT)} (default: ${it.defaultValue()})" }}>",
)
.convert {
val idx = it.indexOf('=')
return@convert if (idx < 0) it to true
else it.substring(0..<idx) to valueToBool(it.substring(idx + 1))
}
.multiple()
.toMap()

private fun TransformContext.valueToBool(value: String): Boolean {
return when (value.lowercase()) {
"true",
"t",
"1",
"yes",
"y",
"on" -> true
"false",
"f",
"0",
"no",
"n",
"off" -> false
else -> fail(context.localization.boolConversionError(value))
}
}

// hidden option used by native tests
private val testPort: Int by
option(names = arrayOf("--test-port"), help = "Internal test option", hidden = true)
Expand Down Expand Up @@ -391,6 +427,7 @@ class BaseOptions : OptionGroup() {
externalResourceReaders = externalResourceReaders.ifEmpty { null },
traceMode = traceMode,
powerAssertionsEnabled = powerAssertionsEnabled,
featureFlags = featureFlags,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ class CliCommandTest {
["foo"] { executable = "foo" }
}
traceMode = "pretty"
featureFlags {
["foo"] = true
["bar"] = false
}
}
"""
.trimIndent()
Expand Down Expand Up @@ -153,6 +157,7 @@ class CliCommandTest {
assertThat(cliTest.myExternalModuleReaders).isEmpty()
assertThat(cliTest.myExternalResourceReaders).isEmpty()
assertThat(builder.traceMode).isEqualTo(TraceMode.COMPACT)
assertThat(builder.featureFlags).isEmpty()
}

// hygiene test to ensure new evaluator settings get covered by the above test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.jspecify.annotations.Nullable;
import org.pkl.config.java.mapper.ValueMapperBuilder;
import org.pkl.core.EvaluatorBuilder;
import org.pkl.core.Logger;
import org.pkl.core.SecurityManager;
import org.pkl.core.StackFrameTransformer;
import org.pkl.core.http.HttpClient;
Expand Down Expand Up @@ -232,6 +233,18 @@ public ConfigEvaluatorBuilder applyFromProject(Project project) {
return this;
}

/**
* Sets the project for the evaluator, and applies any settings if set.
*
* <p>This is a convenience method that delegates to the underlying evaluator builder.
*
* @throws IllegalStateException if {@link #setSecurityManager(SecurityManager)} was also called.
*/
public ConfigEvaluatorBuilder applyFromProject(Project project, Logger logger) {
evaluatorBuilder.applyFromProject(project, logger);
return this;
}

/**
* Sets an evaluation timeout to be enforced by the {@link ConfigEvaluator}'s {@code evaluate}
* methods.
Expand Down
8 changes: 6 additions & 2 deletions pkl-core/src/main/java/org/pkl/core/Analyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class Analyzer {
private final ModuleResolver moduleResolver;
private final HttpClient httpClient;
private final TraceMode traceMode;
private final Map<FeatureFlag, Boolean> featureFlags;

public Analyzer(
StackFrameTransformer transformer,
Expand All @@ -57,7 +58,8 @@ public Analyzer(
@Nullable Path moduleCacheDir,
@Nullable DeclaredDependencies projectDependencies,
HttpClient httpClient,
TraceMode traceMode) {
TraceMode traceMode,
Map<FeatureFlag, Boolean> featureFlags) {
this.transformer = transformer;
this.color = color;
this.securityManager = securityManager;
Expand All @@ -66,6 +68,7 @@ public Analyzer(
this.moduleResolver = new ModuleResolver(moduleKeyFactories);
this.httpClient = httpClient;
this.traceMode = traceMode;
this.featureFlags = featureFlags;
}

/**
Expand Down Expand Up @@ -118,7 +121,8 @@ private Context createContext() {
: new ProjectDependenciesManager(
projectDependencies, moduleResolver, securityManager),
traceMode,
false));
false,
featureFlags));
});
}
}
Loading
Loading