Turn CLI commands into objects, self register subcommands (#935)

Usages of `RootCommand` no longer need to initialize a new instance, nor register subcommands.
This commit is contained in:
Daniel Chao
2025-02-05 09:18:23 -08:00
committed by GitHub
parent aadcccd0fc
commit 9784cd7265
15 changed files with 229 additions and 253 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 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.
@@ -17,26 +17,10 @@
package org.pkl.cli
import com.github.ajalt.clikt.core.subcommands
import org.pkl.cli.commands.*
import org.pkl.commons.cli.cliMain
import org.pkl.core.Release
/** Main method of the Pkl CLI (command-line evaluator and REPL). */
internal fun main(args: Array<String>) {
cliMain {
val version = Release.current().versionInfo()
val helpLink = "${Release.current().documentation().homepage()}pkl-cli/index.html#usage"
RootCommand("pkl", version, helpLink)
.subcommands(
EvalCommand(helpLink),
ReplCommand(helpLink),
ServerCommand(helpLink),
TestCommand(helpLink),
ProjectCommand(helpLink),
DownloadPackageCommand(helpLink),
AnalyzeCommand(helpLink),
)
.main(args)
}
cliMain { RootCommand.main(args) }
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 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.
@@ -25,42 +25,40 @@ import org.pkl.cli.CliImportAnalyzerOptions
import org.pkl.commons.cli.commands.ModulesCommand
import org.pkl.commons.cli.commands.single
class AnalyzeCommand(helpLink: String) :
object AnalyzeCommand :
NoOpCliktCommand(
name = "analyze",
help = "Commands related to static analysis",
epilog = "For more information, visit $helpLink"
epilog = "For more information, visit $helpLink",
) {
init {
subcommands(AnalyzeImportsCommand(helpLink))
}
companion object {
class AnalyzeImportsCommand(helpLink: String) :
ModulesCommand(
name = "imports",
helpLink = helpLink,
help = "Prints the graph of modules imported by the input module(s)."
) {
private val outputPath: Path? by
option(
names = arrayOf("-o", "--output-path"),
metavar = "<path>",
help = "File path where the output file is placed."
)
.path()
.single()
override fun run() {
val options =
CliImportAnalyzerOptions(
base = baseOptions.baseOptions(modules, projectOptions),
outputFormat = baseOptions.format,
outputPath = outputPath
)
CliImportAnalyzer(options).run()
}
}
subcommands(AnalyzeImportsCommand)
}
}
object AnalyzeImportsCommand :
ModulesCommand(
name = "imports",
helpLink = helpLink,
help = "Prints the graph of modules imported by the input module(s).",
) {
private val outputPath: Path? by
option(
names = arrayOf("-o", "--output-path"),
metavar = "<path>",
help = "File path where the output file is placed.",
)
.path()
.single()
override fun run() {
val options =
CliImportAnalyzerOptions(
base = baseOptions.baseOptions(modules, projectOptions),
outputFormat = baseOptions.format,
outputPath = outputPath,
)
CliImportAnalyzer(options).run()
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 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.
@@ -27,7 +27,7 @@ import org.pkl.commons.cli.commands.ProjectOptions
import org.pkl.commons.cli.commands.single
import org.pkl.core.packages.PackageUri
class DownloadPackageCommand(helpLink: String) :
object DownloadPackageCommand :
BaseCommand(
name = "download-package",
helpLink = helpLink,
@@ -44,7 +44,7 @@ class DownloadPackageCommand(helpLink: String) :
$ pkl download-package package://example.com/package1@1.0.0 package://example.com/package2@1.0.0
```
"""
.trimIndent()
.trimIndent(),
) {
private val projectOptions by ProjectOptions()
@@ -56,7 +56,7 @@ class DownloadPackageCommand(helpLink: String) :
private val noTransitive: Boolean by
option(
names = arrayOf("--no-transitive"),
help = "Skip downloading transitive dependencies of a package"
help = "Skip downloading transitive dependencies of a package",
)
.single()
.flag()
@@ -65,7 +65,7 @@ class DownloadPackageCommand(helpLink: String) :
CliPackageDownloader(
baseOptions.baseOptions(emptyList(), projectOptions),
packageUris,
noTransitive
noTransitive,
)
.run()
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 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.
@@ -24,17 +24,13 @@ import org.pkl.cli.CliEvaluatorOptions
import org.pkl.commons.cli.commands.ModulesCommand
import org.pkl.commons.cli.commands.single
class EvalCommand(helpLink: String) :
ModulesCommand(
name = "eval",
help = "Render pkl module(s)",
helpLink = helpLink,
) {
object EvalCommand :
ModulesCommand(name = "eval", help = "Render pkl module(s)", helpLink = helpLink) {
private val outputPath: String? by
option(
names = arrayOf("-o", "--output-path"),
metavar = "<path>",
help = "File path where the output file is placed."
help = "File path where the output file is placed.",
)
.single()
@@ -43,7 +39,7 @@ class EvalCommand(helpLink: String) :
names = arrayOf("--module-output-separator"),
metavar = "<string>",
help =
"Separator to use when multiple module outputs are written to the same file. (default: ---)"
"Separator to use when multiple module outputs are written to the same file. (default: ---)",
)
.single()
.default("---")
@@ -52,7 +48,7 @@ class EvalCommand(helpLink: String) :
option(
names = arrayOf("-x", "--expression"),
metavar = "<expression>",
help = "Expression to be evaluated within the module."
help = "Expression to be evaluated within the module.",
)
.single()
@@ -60,7 +56,7 @@ class EvalCommand(helpLink: String) :
option(
names = arrayOf("-m", "--multiple-file-output-path"),
metavar = "<path>",
help = "Directory where a module's multiple file output is placed."
help = "Directory where a module's multiple file output is placed.",
)
.single()
.validate {
@@ -81,7 +77,7 @@ class EvalCommand(helpLink: String) :
outputFormat = baseOptions.format,
moduleOutputSeparator = moduleOutputSeparator,
multipleFileOutputPath = multipleFileOutputPath,
expression = expression ?: CliEvaluatorOptions.defaults.expression
expression = expression ?: CliEvaluatorOptions.defaults.expression,
)
CliEvaluator(options).run()
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 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.
@@ -31,119 +31,117 @@ import org.pkl.commons.cli.commands.BaseCommand
import org.pkl.commons.cli.commands.TestOptions
import org.pkl.commons.cli.commands.single
class ProjectCommand(helpLink: String) :
private const val NEWLINE = '\u0085'
object ProjectCommand :
NoOpCliktCommand(
name = "project",
help = "Run commands related to projects",
epilog = "For more information, visit $helpLink"
epilog = "For more information, visit $helpLink",
) {
init {
subcommands(ResolveCommand(helpLink), PackageCommand(helpLink))
}
companion object {
class ResolveCommand(helpLink: String) :
BaseCommand(
name = "resolve",
helpLink = helpLink,
help =
"""
Resolve dependencies for project(s)
This command takes the `dependencies` of `PklProject`s, and writes the
resolved versions to `PklProject.deps.json` files.
Examples:
```
# Search the current working directory for a project, and resolve its dependencies.
$ pkl project resolve
# Resolve dependencies for all projects within the `packages/` directory.
$ pkl project resolve packages/*/
```
""",
) {
private val projectDirs: List<Path> by
argument("<dir>", "The project directories to resolve dependencies for").path().multiple()
override fun run() {
CliProjectResolver(baseOptions.baseOptions(emptyList()), projectDirs).run()
}
}
private const val NEWLINE = '\u0085'
class PackageCommand(helpLink: String) :
BaseCommand(
name = "package",
helpLink = helpLink,
help =
"""
Verify package(s), and prepare package artifacts to be published.
This command runs a project's api tests, as defined by `apiTests` in `PklProject`.
Additionally, it verifies that all imports resolve to paths that are local to the project.
Finally, this command writes the following artifacts into the output directory specified by the output path.
- `name@version` - dependency metadata$NEWLINE
- `name@version.sha256` - dependency metadata's SHA-256 checksum$NEWLINE
- `name@version.zip` - package archive$NEWLINE
- `name@version.zip.sha256` - package archive's SHA-256 checksum
The output path option accepts the following placeholders:
- %{name}: The display name of the package$NEWLINE
- %{version}: The version of the package
If a project has local project dependencies, the depended upon project directories must also
be included as arguments to this command.
Examples:
```
# Search the current working directory for a project, and package it.
$ pkl project package
# Package all projects within the `packages/` directory.
$ pkl project package packages/*/
```
"""
.trimIndent(),
) {
private val testOptions by TestOptions()
private val projectDirs: List<Path> by
argument("<dir>", "The project directories to package").path().multiple()
private val outputPath: String by
option(
names = arrayOf("--output-path"),
help = "The directory to write artifacts to",
metavar = "<path>"
)
.single()
.default(".out/%{name}@%{version}")
private val skipPublishCheck: Boolean by
option(
names = arrayOf("--skip-publish-check"),
help = "Skip checking if a package has already been published with different contents",
)
.single()
.flag()
override fun run() {
CliProjectPackager(
baseOptions.baseOptions(emptyList()),
projectDirs,
testOptions.cliTestOptions,
outputPath,
skipPublishCheck
)
.run()
}
}
subcommands(ResolveCommand, PackageCommand)
}
}
object ResolveCommand :
BaseCommand(
name = "resolve",
helpLink = helpLink,
help =
"""
Resolve dependencies for project(s)
This command takes the `dependencies` of `PklProject`s, and writes the
resolved versions to `PklProject.deps.json` files.
Examples:
```
# Search the current working directory for a project, and resolve its dependencies.
$ pkl project resolve
# Resolve dependencies for all projects within the `packages/` directory.
$ pkl project resolve packages/*/
```
""",
) {
private val projectDirs: List<Path> by
argument("<dir>", "The project directories to resolve dependencies for").path().multiple()
override fun run() {
CliProjectResolver(baseOptions.baseOptions(emptyList()), projectDirs).run()
}
}
object PackageCommand :
BaseCommand(
name = "package",
helpLink = helpLink,
help =
"""
Verify package(s), and prepare package artifacts to be published.
This command runs a project's api tests, as defined by `apiTests` in `PklProject`.
Additionally, it verifies that all imports resolve to paths that are local to the project.
Finally, this command writes the following artifacts into the output directory specified by the output path.
- `name@version` - dependency metadata$NEWLINE
- `name@version.sha256` - dependency metadata's SHA-256 checksum$NEWLINE
- `name@version.zip` - package archive$NEWLINE
- `name@version.zip.sha256` - package archive's SHA-256 checksum
The output path option accepts the following placeholders:
- %{name}: The display name of the package$NEWLINE
- %{version}: The version of the package
If a project has local project dependencies, the depended upon project directories must also
be included as arguments to this command.
Examples:
```
# Search the current working directory for a project, and package it.
$ pkl project package
# Package all projects within the `packages/` directory.
$ pkl project package packages/*/
```
"""
.trimIndent(),
) {
private val testOptions by TestOptions()
private val projectDirs: List<Path> by
argument("<dir>", "The project directories to package").path().multiple()
private val outputPath: String by
option(
names = arrayOf("--output-path"),
help = "The directory to write artifacts to",
metavar = "<path>",
)
.single()
.default(".out/%{name}@%{version}")
private val skipPublishCheck: Boolean by
option(
names = arrayOf("--skip-publish-check"),
help = "Skip checking if a package has already been published with different contents",
)
.single()
.flag()
override fun run() {
CliProjectPackager(
baseOptions.baseOptions(emptyList()),
projectDirs,
testOptions.cliTestOptions,
outputPath,
skipPublishCheck,
)
.run()
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 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.
@@ -21,12 +21,8 @@ import org.pkl.cli.CliRepl
import org.pkl.commons.cli.commands.BaseCommand
import org.pkl.commons.cli.commands.ProjectOptions
class ReplCommand(helpLink: String) :
BaseCommand(
name = "repl",
help = "Start a REPL session",
helpLink = helpLink,
) {
object ReplCommand :
BaseCommand(name = "repl", help = "Start a REPL session", helpLink = helpLink) {
private val projectOptions by ProjectOptions()
override fun run() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 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.
@@ -17,16 +17,20 @@ package org.pkl.cli.commands
import com.github.ajalt.clikt.core.NoOpCliktCommand
import com.github.ajalt.clikt.core.context
import com.github.ajalt.clikt.core.subcommands
import com.github.ajalt.clikt.parameters.options.versionOption
import org.pkl.core.Release
class RootCommand(name: String, version: String, helpLink: String) :
internal val helpLink = "${Release.current().documentation.homepage}pkl-cli/index.html#usage"
object RootCommand :
NoOpCliktCommand(
name = name,
name = "pkl",
printHelpOnEmptyArgs = true,
epilog = "For more information, visit $helpLink",
) {
init {
versionOption(version, names = setOf("-v", "--version"), message = { it })
versionOption(Release.current().versionInfo, names = setOf("-v", "--version"), message = { it })
context {
correctionSuggestor = { given, possible ->
@@ -35,5 +39,15 @@ class RootCommand(name: String, version: String, helpLink: String) :
} else possible
}
}
subcommands(
EvalCommand,
ReplCommand,
ServerCommand,
TestCommand,
ProjectCommand,
DownloadPackageCommand,
AnalyzeCommand,
)
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 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.
@@ -19,11 +19,11 @@ import com.github.ajalt.clikt.core.CliktCommand
import org.pkl.cli.CliServer
import org.pkl.commons.cli.CliBaseOptions
class ServerCommand(helpLink: String) :
object ServerCommand :
CliktCommand(
name = "server",
help = "Run as a server that communicates over standard input/output",
epilog = "For more information, visit $helpLink"
epilog = "For more information, visit $helpLink",
) {
override fun run() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 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.
@@ -26,7 +26,7 @@ import org.pkl.commons.cli.commands.BaseOptions
import org.pkl.commons.cli.commands.ProjectOptions
import org.pkl.commons.cli.commands.TestOptions
class TestCommand(helpLink: String) :
object TestCommand :
BaseCommand(name = "test", help = "Run tests within the given module(s)", helpLink = helpLink) {
val modules: List<URI> by
argument(name = "<modules>", help = "Module paths or URIs to evaluate.")
@@ -40,7 +40,7 @@ class TestCommand(helpLink: String) :
override fun run() {
CliTestRunner(
options = baseOptions.baseOptions(modules, projectOptions),
testOptions = testOptions.cliTestOptions
testOptions = testOptions.cliTestOptions,
)
.run()
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 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.
@@ -16,7 +16,6 @@
package org.pkl.cli
import com.github.ajalt.clikt.core.BadParameterValue
import com.github.ajalt.clikt.core.subcommands
import java.nio.file.Path
import kotlin.io.path.createDirectory
import kotlin.io.path.createSymbolicLinkPointingTo
@@ -27,35 +26,34 @@ import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.condition.DisabledOnOs
import org.junit.jupiter.api.condition.OS
import org.junit.jupiter.api.io.TempDir
import org.pkl.cli.commands.AnalyzeCommand
import org.pkl.cli.commands.EvalCommand
import org.pkl.cli.commands.RootCommand
import org.pkl.commons.writeString
import org.pkl.core.Release
class CliMainTest {
private val evalCmd = EvalCommand("")
private val analyzeCommand = AnalyzeCommand("")
private val cmd = RootCommand("pkl", "pkl version 1", "").subcommands(evalCmd, analyzeCommand)
@Test
fun `duplicate CLI option produces meaningful error message`(@TempDir tempDir: Path) {
val inputFile = tempDir.resolve("test.pkl").writeString("").toString()
assertThatCode {
cmd.parse(arrayOf("eval", "--output-path", "path1", "--output-path", "path2", inputFile))
RootCommand.parse(
arrayOf("eval", "--output-path", "path1", "--output-path", "path2", inputFile)
)
}
.hasMessage("Invalid value for \"--output-path\": Option cannot be repeated")
assertThatCode {
cmd.parse(arrayOf("eval", "-o", "path1", "--output-path", "path2", inputFile))
RootCommand.parse(arrayOf("eval", "-o", "path1", "--output-path", "path2", inputFile))
}
.hasMessage("Invalid value for \"--output-path\": Option cannot be repeated")
}
@Test
fun `eval requires at least one file`() {
assertThatCode { cmd.parse(arrayOf("eval")) }.hasMessage("""Missing argument "<modules>"""")
assertThatCode { RootCommand.parse(arrayOf("eval")) }
.hasMessage("""Missing argument "<modules>"""")
}
// Can't reliably create symlinks on Windows.
@@ -76,7 +74,7 @@ class CliMainTest {
val inputFile = tempDir.resolve("test.pkl").writeString(code).toString()
val outputFile = makeSymdir(tempDir, "out", "linkOut").resolve("test.pkl").toString()
assertThatCode { cmd.parse(arrayOf("eval", inputFile, "-o", outputFile)) }
assertThatCode { RootCommand.parse(arrayOf("eval", inputFile, "-o", outputFile)) }
.doesNotThrowAnyException()
}
@@ -87,22 +85,24 @@ class CliMainTest {
val error =
"""Invalid value for "--multiple-file-output-path": Option is mutually exclusive with -o, --output-path and -x, --expression."""
assertThatCode { cmd.parse(arrayOf("eval", "-m", testOut, "-x", "x", testIn)) }
assertThatCode { RootCommand.parse(arrayOf("eval", "-m", testOut, "-x", "x", testIn)) }
.hasMessage(error)
assertThatCode { cmd.parse(arrayOf("eval", "-m", testOut, "-o", "/tmp/test", testIn)) }
assertThatCode { RootCommand.parse(arrayOf("eval", "-m", testOut, "-o", "/tmp/test", testIn)) }
.hasMessage(error)
}
@Test
fun `showing version works`() {
assertThatCode { cmd.parse(arrayOf("--version")) }.hasMessage("pkl version 1")
assertThatCode { RootCommand.parse(arrayOf("--version")) }
.hasMessage(Release.current().versionInfo)
}
@Test
fun `file paths get parsed into URIs`(@TempDir tempDir: Path) {
cmd.parse(arrayOf("eval", makeInput(tempDir, "my file.txt")))
RootCommand.parse(arrayOf("eval", makeInput(tempDir, "my file.txt")))
val evalCmd = RootCommand.registeredSubcommands().filterIsInstance<EvalCommand>().first()
val modules = evalCmd.baseOptions.baseOptions(evalCmd.modules).normalizedSourceModules
assertThat(modules).hasSize(1)
assertThat(modules[0].path).endsWith("my file.txt")
@@ -110,7 +110,8 @@ class CliMainTest {
@Test
fun `invalid URIs are not accepted`() {
val ex = assertThrows<BadParameterValue> { cmd.parse(arrayOf("eval", "file:my file.txt")) }
val ex =
assertThrows<BadParameterValue> { RootCommand.parse(arrayOf("eval", "file:my file.txt")) }
assertThat(ex.message).contains("URI `file:my file.txt` has invalid syntax")
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 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.
@@ -16,18 +16,16 @@
package org.pkl.cli
import com.github.ajalt.clikt.core.MissingArgument
import com.github.ajalt.clikt.core.subcommands
import java.io.StringWriter
import java.io.Writer
import java.net.URI
import java.nio.file.Path
import java.util.Locale
import java.util.*
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatCode
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.io.TempDir
import org.pkl.cli.commands.EvalCommand
import org.pkl.cli.commands.RootCommand
import org.pkl.commons.cli.CliBaseOptions
import org.pkl.commons.cli.CliException
@@ -35,7 +33,6 @@ import org.pkl.commons.cli.CliTestOptions
import org.pkl.commons.readString
import org.pkl.commons.toUri
import org.pkl.commons.writeString
import org.pkl.core.Release
class CliTestRunnerTest {
@Test
@@ -381,7 +378,7 @@ class CliTestRunnerTest {
val opts =
CliBaseOptions(
sourceModules = listOf(input.toUri(), input2.toUri()),
settings = URI("pkl:settings")
settings = URI("pkl:settings"),
)
val testOpts = CliTestOptions(junitDir = tempDir)
val runner = CliTestRunner(opts, testOpts, noopWriter, noopWriter)
@@ -391,12 +388,7 @@ class CliTestRunnerTest {
@Test
fun `no source modules specified has same message as pkl eval`() {
val e1 = assertThrows<CliException> { CliTestRunner(CliBaseOptions(), CliTestOptions()).run() }
val e2 =
assertThrows<MissingArgument> {
val rootCommand =
RootCommand("pkl", Release.current().versionInfo(), "").subcommands(EvalCommand(""))
rootCommand.parse(listOf("eval"))
}
val e2 = assertThrows<MissingArgument> { RootCommand.parse(listOf("eval")) }
assertThat(e1).hasMessageContaining("Missing argument \"<modules>\"")
assertThat(e1.message!!.replace("test", "eval")).isEqualTo(e2.helpMessage())
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 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.
@@ -28,10 +28,10 @@ import org.pkl.core.Release
/** Main method for the Java code generator CLI. */
internal fun main(args: Array<String>) {
cliMain { PklJavaCodegenCommand().main(args) }
cliMain { PklJavaCodegenCommand.main(args) }
}
class PklJavaCodegenCommand :
object PklJavaCodegenCommand :
ModulesCommand(
name = "pkl-codegen-java",
helpLink = Release.current().documentation().homepage(),
@@ -43,7 +43,7 @@ class PklJavaCodegenCommand :
option(
names = arrayOf("-o", "--output-dir"),
metavar = "<path>",
help = "The directory where generated source code is placed."
help = "The directory where generated source code is placed.",
)
.path()
.default(defaults.outputDir)
@@ -52,7 +52,7 @@ class PklJavaCodegenCommand :
option(
names = arrayOf("--indent"),
metavar = "<chars>",
help = "The characters to use for indenting generated source code."
help = "The characters to use for indenting generated source code.",
)
.default(defaults.indent)
@@ -61,21 +61,21 @@ class PklJavaCodegenCommand :
names = arrayOf("--generate-getters"),
help =
"Whether to generate public getter methods and " +
"private final fields instead of public final fields."
"private final fields instead of public final fields.",
)
.flag()
private val generateJavadoc: Boolean by
option(
names = arrayOf("--generate-javadoc"),
help = "Whether to preserve Pkl doc comments by generating corresponding Javadoc comments."
help = "Whether to preserve Pkl doc comments by generating corresponding Javadoc comments.",
)
.flag()
private val generateSpringBoot: Boolean by
option(
names = arrayOf("--generate-spring-boot"),
help = "Whether to generate config classes for use with Spring Boot."
help = "Whether to generate config classes for use with Spring Boot.",
)
.flag()
@@ -83,7 +83,7 @@ class PklJavaCodegenCommand :
option(
names = arrayOf("--params-annotation"),
help =
"Fully qualified name of the annotation type to use for annotating constructor parameters with their name."
"Fully qualified name of the annotation type to use for annotating constructor parameters with their name.",
)
.defaultLazy(
"`none` if `--generate-spring-boot` is set, `org.pkl.config.java.mapper.Named` otherwise"
@@ -100,13 +100,13 @@ class PklJavaCodegenCommand :
The specified annotation type must be annotated with `@java.lang.annotation.Target(ElementType.TYPE_USE)`
or the generated code may not compile.
"""
.trimIndent()
.trimIndent(),
)
private val implementSerializable: Boolean by
option(
names = arrayOf("--implement-serializable"),
help = "Whether to generate classes that implement java.io.Serializable."
help = "Whether to generate classes that implement java.io.Serializable.",
)
.flag()
@@ -121,7 +121,7 @@ class PklJavaCodegenCommand :
With this option, you can override or modify the default names, renaming entire
classes or just their packages.
"""
.trimIndent()
.trimIndent(),
)
.associate()
@@ -137,7 +137,7 @@ class PklJavaCodegenCommand :
paramsAnnotation = if (paramsAnnotation == "none") null else paramsAnnotation,
nonNullAnnotation = nonNullAnnotation,
implementSerializable = implementSerializable,
renames = renames
renames = renames,
)
CliJavaCodeGenerator(options).run()
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 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.
@@ -31,10 +31,10 @@ import org.pkl.core.Release
/** Main method for the Kotlin code generator CLI. */
internal fun main(args: Array<String>) {
cliMain { PklKotlinCodegenCommand().main(args) }
cliMain { PklKotlinCodegenCommand.main(args) }
}
class PklKotlinCodegenCommand :
object PklKotlinCodegenCommand :
ModulesCommand(
name = "pkl-codegen-kotlin",
helpLink = Release.current().documentation().homepage(),
@@ -46,7 +46,7 @@ class PklKotlinCodegenCommand :
option(
names = arrayOf("-o", "--output-dir"),
metavar = "<path>",
help = "The directory where generated source code is placed."
help = "The directory where generated source code is placed.",
)
.path()
.default(defaults.outputDir)
@@ -55,28 +55,28 @@ class PklKotlinCodegenCommand :
option(
names = arrayOf("--indent"),
metavar = "<chars>",
help = "The characters to use for indenting generated source code."
help = "The characters to use for indenting generated source code.",
)
.default(defaults.indent)
private val generateKdoc: Boolean by
option(
names = arrayOf("--generate-kdoc"),
help = "Whether to preserve Pkl doc comments by generating corresponding KDoc comments."
help = "Whether to preserve Pkl doc comments by generating corresponding KDoc comments.",
)
.flag()
private val generateSpringboot: Boolean by
option(
names = arrayOf("--generate-spring-boot"),
help = "Whether to generate config classes for use with Spring Boot."
help = "Whether to generate config classes for use with Spring Boot.",
)
.flag()
private val implementSerializable: Boolean by
option(
names = arrayOf("--implement-serializable"),
help = "Whether to generate classes that implement java.io.Serializable."
help = "Whether to generate classes that implement java.io.Serializable.",
)
.flag()
@@ -91,7 +91,7 @@ class PklKotlinCodegenCommand :
With this option, you can override or modify the default names, renaming entire
classes or just their packages.
"""
.trimIndent()
.trimIndent(),
)
.associate()
@@ -104,7 +104,7 @@ class PklKotlinCodegenCommand :
generateKdoc = generateKdoc,
generateSpringBootConfig = generateSpringboot,
implementSerializable = implementSerializable,
renames = renames
renames = renames,
)
CliKotlinCodeGenerator(options).run()
}

View File

@@ -34,10 +34,10 @@ import org.pkl.core.Release
/** Main method for the Pkldoc CLI. */
internal fun main(args: Array<String>) {
cliMain { DocCommand().main(args) }
cliMain { DocCommand.main(args) }
}
class DocCommand :
object DocCommand :
BaseCommand(name = "pkldoc", helpLink = Release.current().documentation().homepage()) {
private val modules: List<URI> by

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 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.
@@ -21,12 +21,9 @@ import org.junit.jupiter.api.assertThrows
import org.pkl.commons.cli.CliException
class CliMainTest {
private val docCommand = DocCommand()
@Test
fun `CLI run test`() {
val e = assertThrows<CliException> { docCommand.parse(arrayOf("foo", "--output-dir", "/tmp")) }
val e = assertThrows<CliException> { DocCommand.parse(arrayOf("foo", "--output-dir", "/tmp")) }
assertThat(e)
.hasMessageContaining("must contain at least one module named `doc-package-info.pkl`")
}