mirror of
https://github.com/apple/pkl.git
synced 2026-05-04 22:24:43 +02:00
Add analyze imports libs (SPICE-0001) (#695)
This adds a new feature to build a dependency graph of Pkl programs, following the SPICE outlined in https://github.com/apple/pkl-evolution/pull/2. It adds: * CLI command `pkl analyze imports` * Java API `org.pkl.core.Analyzer` * Pkl stdlib module `pkl:analyze` * pkl-gradle extension `analyze` In addition, it also changes the Gradle plugin such that `transitiveModules` is by default computed from the import graph.
This commit is contained in:
142
pkl-cli/src/test/kotlin/org/pkl/cli/CliImportAnalyzerTest.kt
Normal file
142
pkl-cli/src/test/kotlin/org/pkl/cli/CliImportAnalyzerTest.kt
Normal file
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Copyright © 2024 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.cli
|
||||
|
||||
import java.net.URI
|
||||
import java.nio.file.Path
|
||||
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.io.TempDir
|
||||
import org.pkl.commons.cli.CliBaseOptions
|
||||
import org.pkl.commons.writeString
|
||||
import org.pkl.core.OutputFormat
|
||||
import org.pkl.core.util.StringBuilderWriter
|
||||
|
||||
class CliImportAnalyzerTest {
|
||||
@Test
|
||||
fun `write to console writer`(@TempDir tempDir: Path) {
|
||||
val file = tempDir.resolve("test.pkl").writeString("import \"bar.pkl\"")
|
||||
val otherFile = tempDir.resolve("bar.pkl").writeString("")
|
||||
val baseOptions = CliBaseOptions(sourceModules = listOf(file.toUri()))
|
||||
val sb = StringBuilder()
|
||||
val analyzer = CliImportAnalyzer(CliImportAnalyzerOptions(baseOptions), StringBuilderWriter(sb))
|
||||
analyzer.run()
|
||||
assertThat(sb.toString())
|
||||
.isEqualTo(
|
||||
"""
|
||||
imports {
|
||||
["${otherFile.toUri()}"] {}
|
||||
["${file.toUri()}"] {
|
||||
new {
|
||||
uri = "${otherFile.toUri()}"
|
||||
}
|
||||
}
|
||||
}
|
||||
resolvedImports {
|
||||
["${otherFile.toUri()}"] = "${otherFile.toRealPath().toUri()}"
|
||||
["${file.toUri()}"] = "${file.toRealPath().toUri()}"
|
||||
}
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `different output format`(@TempDir tempDir: Path) {
|
||||
val file = tempDir.resolve("test.pkl").writeString("import \"bar.pkl\"")
|
||||
val otherFile = tempDir.resolve("bar.pkl").writeString("")
|
||||
val baseOptions = CliBaseOptions(sourceModules = listOf(file.toUri()))
|
||||
val sb = StringBuilder()
|
||||
val analyzer =
|
||||
CliImportAnalyzer(
|
||||
CliImportAnalyzerOptions(baseOptions, outputFormat = OutputFormat.JSON.toString()),
|
||||
StringBuilderWriter(sb)
|
||||
)
|
||||
analyzer.run()
|
||||
assertThat(sb.toString())
|
||||
.isEqualTo(
|
||||
"""
|
||||
{
|
||||
"imports": {
|
||||
"${otherFile.toUri()}": [],
|
||||
"${file.toUri()}": [
|
||||
{
|
||||
"uri": "${otherFile.toUri()}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"resolvedImports": {
|
||||
"${otherFile.toUri()}": "${otherFile.toRealPath().toUri()}",
|
||||
"${file.toUri()}": "${file.toRealPath().toUri()}"
|
||||
}
|
||||
}
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `write to output file`(@TempDir tempDir: Path) {
|
||||
val file = tempDir.resolve("test.pkl").writeString("import \"bar.pkl\"")
|
||||
val otherFile = tempDir.resolve("bar.pkl").writeString("")
|
||||
val outputPath = tempDir.resolve("imports.pcf")
|
||||
val baseOptions = CliBaseOptions(sourceModules = listOf(file.toUri()))
|
||||
val analyzer = CliImportAnalyzer(CliImportAnalyzerOptions(baseOptions, outputPath = outputPath))
|
||||
analyzer.run()
|
||||
assertThat(outputPath)
|
||||
.hasContent(
|
||||
"""
|
||||
imports {
|
||||
["${otherFile.toUri()}"] {}
|
||||
["${file.toUri()}"] {
|
||||
new {
|
||||
uri = "${otherFile.toUri()}"
|
||||
}
|
||||
}
|
||||
}
|
||||
resolvedImports {
|
||||
["${otherFile.toUri()}"] = "${otherFile.toRealPath().toUri()}"
|
||||
["${file.toUri()}"] = "${file.toRealPath().toUri()}"
|
||||
}
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invalid syntax in module`(@TempDir tempDir: Path) {
|
||||
val file = tempDir.resolve("test.pkl").writeString("foo = bar(]")
|
||||
assertThatCode {
|
||||
CliImportAnalyzer(
|
||||
CliImportAnalyzerOptions(
|
||||
CliBaseOptions(sourceModules = listOf(file.toUri()), settings = URI("pkl:settings"))
|
||||
)
|
||||
)
|
||||
.run()
|
||||
}
|
||||
.hasMessageContaining(
|
||||
"""
|
||||
–– Pkl Error ––
|
||||
Found a syntax error when parsing module `${file.toUri()}`.
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ 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
|
||||
@@ -34,7 +35,8 @@ import org.pkl.commons.writeString
|
||||
class CliMainTest {
|
||||
|
||||
private val evalCmd = EvalCommand("")
|
||||
private val cmd = RootCommand("pkl", "pkl version 1", "").subcommands(evalCmd)
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user