mirror of
https://github.com/apple/pkl.git
synced 2026-05-25 16:19:20 +02:00
pkl-gradle: Migrate nullness to jSpecify (#1530)
This commit is contained in:
@@ -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.
|
||||
@@ -36,12 +36,11 @@ import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin;
|
||||
import org.gradle.plugins.ide.idea.model.IdeaModel;
|
||||
import org.gradle.util.GradleVersion;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.cli.CliEvaluatorOptions;
|
||||
import org.pkl.core.ImportGraph;
|
||||
import org.pkl.core.OutputFormat;
|
||||
import org.pkl.core.util.IoUtils;
|
||||
import org.pkl.core.util.LateInit;
|
||||
import org.pkl.core.util.Nullable;
|
||||
import org.pkl.gradle.spec.AnalyzeImportsSpec;
|
||||
import org.pkl.gradle.spec.BasePklSpec;
|
||||
import org.pkl.gradle.spec.CodeGenSpec;
|
||||
@@ -71,21 +70,26 @@ public class PklPlugin implements Plugin<Project> {
|
||||
|
||||
private static final String MIN_GRADLE_VERSION = "8.2";
|
||||
|
||||
@LateInit private Project project;
|
||||
private @Nullable Project __project;
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
this.project = project;
|
||||
__project = project;
|
||||
|
||||
if (GradleVersion.current().compareTo(GradleVersion.version(MIN_GRADLE_VERSION)) < 0) {
|
||||
throw new GradleException(
|
||||
String.format("Plugin `org.pkl` requires Gradle %s or higher.", MIN_GRADLE_VERSION));
|
||||
}
|
||||
|
||||
var extension = project.getExtensions().create("pkl", PklExtension.class);
|
||||
var extension = project().getExtensions().create("pkl", PklExtension.class);
|
||||
configureExtension(extension);
|
||||
}
|
||||
|
||||
private Project project() {
|
||||
assert __project != null;
|
||||
return __project;
|
||||
}
|
||||
|
||||
private void configureExtension(PklExtension extension) {
|
||||
configureEvalTasks(extension.getEvaluators());
|
||||
configureJavaCodeGenTasks(extension.getJavaCodeGenerators());
|
||||
@@ -102,7 +106,7 @@ public class PklPlugin implements Plugin<Project> {
|
||||
spec -> {
|
||||
configureBaseSpec(spec);
|
||||
spec.getOutputPath()
|
||||
.convention(project.getLayout().getBuildDirectory().dir("generated/pkl/packages"));
|
||||
.convention(project().getLayout().getBuildDirectory().dir("generated/pkl/packages"));
|
||||
spec.getOverwrite().convention(false);
|
||||
var packageTask = createTask(ProjectPackageTask.class, spec);
|
||||
packageTask.configure(
|
||||
@@ -113,12 +117,12 @@ public class PklPlugin implements Plugin<Project> {
|
||||
task.getJunitReportsDir().set(spec.getJunitReportsDir());
|
||||
task.getOverwrite().set(spec.getOverwrite());
|
||||
});
|
||||
project
|
||||
project()
|
||||
.getPluginManager()
|
||||
.withPlugin(
|
||||
"base",
|
||||
appliedPlugin ->
|
||||
project
|
||||
project()
|
||||
.getTasks()
|
||||
.named(
|
||||
LifecycleBasePlugin.BUILD_TASK_NAME,
|
||||
@@ -157,7 +161,7 @@ public class PklPlugin implements Plugin<Project> {
|
||||
configureBaseSpec(spec);
|
||||
spec.getOutputFile()
|
||||
.convention(
|
||||
project
|
||||
project()
|
||||
.getLayout()
|
||||
.getProjectDirectory()
|
||||
// %{moduleDir} is resolved relatively to the working directory,
|
||||
@@ -194,11 +198,12 @@ public class PklPlugin implements Plugin<Project> {
|
||||
// constructor parameters annotations by setting this property to `null`.
|
||||
spec.getParamsAnnotation()
|
||||
.set(
|
||||
project.provider(
|
||||
() ->
|
||||
spec.getGenerateSpringBootConfig().get()
|
||||
? null
|
||||
: "org.pkl.config.java.mapper.Named"));
|
||||
project()
|
||||
.provider(
|
||||
() ->
|
||||
spec.getGenerateSpringBootConfig().get()
|
||||
? null
|
||||
: "org.pkl.config.java.mapper.Named"));
|
||||
|
||||
createModulesTask(JavaCodeGenTask.class, spec)
|
||||
.configure(
|
||||
@@ -211,14 +216,15 @@ public class PklPlugin implements Plugin<Project> {
|
||||
});
|
||||
});
|
||||
|
||||
project.afterEvaluate(
|
||||
prj ->
|
||||
specs.all(
|
||||
spec -> {
|
||||
configureIdeaModule(spec);
|
||||
configureCodeGenSpecSourceDirectories(
|
||||
spec, "java", s -> Optional.of(s.getJava()));
|
||||
}));
|
||||
project()
|
||||
.afterEvaluate(
|
||||
prj ->
|
||||
specs.all(
|
||||
spec -> {
|
||||
configureIdeaModule(spec);
|
||||
configureCodeGenSpecSourceDirectories(
|
||||
spec, "java", s -> Optional.of(s.getJava()));
|
||||
}));
|
||||
}
|
||||
|
||||
private void configureKotlinCodeGenTasks(NamedDomainObjectContainer<KotlinCodeGenSpec> specs) {
|
||||
@@ -237,14 +243,15 @@ public class PklPlugin implements Plugin<Project> {
|
||||
});
|
||||
});
|
||||
|
||||
project.afterEvaluate(
|
||||
prj ->
|
||||
specs.all(
|
||||
spec -> {
|
||||
configureIdeaModule(spec);
|
||||
configureCodeGenSpecSourceDirectories(
|
||||
spec, "kotlin", this::getKotlinSourceDirectorySet);
|
||||
}));
|
||||
project()
|
||||
.afterEvaluate(
|
||||
prj ->
|
||||
specs.all(
|
||||
spec -> {
|
||||
configureIdeaModule(spec);
|
||||
configureCodeGenSpecSourceDirectories(
|
||||
spec, "kotlin", this::getKotlinSourceDirectorySet);
|
||||
}));
|
||||
}
|
||||
|
||||
private void configurePkldocTasks(NamedDomainObjectContainer<PkldocSpec> specs) {
|
||||
@@ -254,7 +261,7 @@ public class PklPlugin implements Plugin<Project> {
|
||||
|
||||
spec.getOutputDir()
|
||||
.convention(
|
||||
project
|
||||
project()
|
||||
.getLayout()
|
||||
.getBuildDirectory()
|
||||
.map(it -> it.dir("pkldoc").dir(spec.getName())));
|
||||
@@ -284,12 +291,12 @@ public class PklPlugin implements Plugin<Project> {
|
||||
task.getOverwrite().set(spec.getOverwrite());
|
||||
});
|
||||
|
||||
project
|
||||
project()
|
||||
.getPluginManager()
|
||||
.withPlugin(
|
||||
"base",
|
||||
appliedPlugin ->
|
||||
project
|
||||
project()
|
||||
.getTasks()
|
||||
.named(
|
||||
LifecycleBasePlugin.CHECK_TASK_NAME,
|
||||
@@ -306,7 +313,7 @@ public class PklPlugin implements Plugin<Project> {
|
||||
spec.getAllowedResources()
|
||||
.convention(List.of("env:", "prop:", "file:", "modulepath:", "https:", "package:"));
|
||||
|
||||
spec.getEvalRootDir().convention(project.getRootProject().getLayout().getProjectDirectory());
|
||||
spec.getEvalRootDir().convention(project().getRootProject().getLayout().getProjectDirectory());
|
||||
|
||||
// Defaulting to OS env vars is bad for reproducibility and cachability.
|
||||
// Hence, this spec defaults to empty env vars, which is consistent with other Gradle tasks
|
||||
@@ -326,18 +333,19 @@ public class PklPlugin implements Plugin<Project> {
|
||||
private void configureCodeGenSpec(CodeGenSpec spec) {
|
||||
spec.getOutputDir()
|
||||
.convention(
|
||||
project
|
||||
project()
|
||||
.getLayout()
|
||||
.getBuildDirectory()
|
||||
.map(it -> it.dir("generated").dir("pkl").dir(spec.getName())));
|
||||
|
||||
spec.getSourceSet()
|
||||
.convention(
|
||||
project
|
||||
project()
|
||||
.getProviders()
|
||||
.provider(
|
||||
() -> {
|
||||
var sourceSets = project.getExtensions().findByType(SourceSetContainer.class);
|
||||
var sourceSets =
|
||||
project().getExtensions().findByType(SourceSetContainer.class);
|
||||
if (sourceSets == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -371,7 +379,7 @@ public class PklPlugin implements Plugin<Project> {
|
||||
// Refer to configureCodeGenSpecSourceDirectories for logic which links the codegen task
|
||||
// to sourceSet.getResources().getSourceDirectories().
|
||||
|
||||
var modulePath = project.files();
|
||||
var modulePath = project().files();
|
||||
modulePath
|
||||
.from(getResourceSourceDirectoriesExceptSpecOutput(spec))
|
||||
// This technically breaks the dependency on compile classpath builder tasks,
|
||||
@@ -411,14 +419,14 @@ public class PklPlugin implements Plugin<Project> {
|
||||
String languageName,
|
||||
Function<? super SourceSet, ? extends Optional<SourceDirectorySet>>
|
||||
extractSourceDirectorySet) {
|
||||
var task = project.getTasks().named(spec.getName(), CodeGenTask.class);
|
||||
var task = project().getTasks().named(spec.getName(), CodeGenTask.class);
|
||||
var sourceSet = spec.getSourceSet().get();
|
||||
extractSourceDirectorySet
|
||||
.apply(sourceSet)
|
||||
.ifPresentOrElse(
|
||||
dirSet -> dirSet.srcDir(task.flatMap(t -> t.getOutputDir().dir(languageName))),
|
||||
() ->
|
||||
project
|
||||
project()
|
||||
.getLogger()
|
||||
.debug(
|
||||
"Source directory set for language {} is not available, "
|
||||
@@ -431,12 +439,12 @@ public class PklPlugin implements Plugin<Project> {
|
||||
// Must be called from Project.afterEvaluate() only, because this method depends
|
||||
// on user-provided configuration not accessible with lazy configuration.
|
||||
private void configureIdeaModule(CodeGenSpec spec) {
|
||||
project
|
||||
project()
|
||||
.getPluginManager()
|
||||
.withPlugin(
|
||||
"idea",
|
||||
plugin -> {
|
||||
var module = project.getExtensions().getByType(IdeaModel.class).getModule();
|
||||
var module = project().getExtensions().getByType(IdeaModel.class).getModule();
|
||||
var outputDir = spec.getOutputDir().get().getAsFile();
|
||||
module.getGeneratedSourceDirs().add(outputDir);
|
||||
if (spec.getSourceSet().get().getName().toLowerCase().contains("test")) {
|
||||
@@ -521,11 +529,11 @@ public class PklPlugin implements Plugin<Project> {
|
||||
|
||||
private TaskProvider<AnalyzeImportsTask> createAnalyzeImportsTask(ModulesSpec spec) {
|
||||
var outputFile =
|
||||
project
|
||||
project()
|
||||
.getLayout()
|
||||
.getBuildDirectory()
|
||||
.file("pkl-gradle/imports/" + spec.getName() + ".json");
|
||||
return project
|
||||
return project()
|
||||
.getTasks()
|
||||
.register(
|
||||
spec.getName() + "GatherImports",
|
||||
@@ -567,7 +575,7 @@ public class PklPlugin implements Plugin<Project> {
|
||||
private <T extends ModulesTask> TaskProvider<T> createModulesTask(
|
||||
Class<T> taskClass, ModulesSpec spec) {
|
||||
var analyzeImportsTask = createAnalyzeImportsTask(spec);
|
||||
return project
|
||||
return project()
|
||||
.getTasks()
|
||||
.register(
|
||||
spec.getName(),
|
||||
@@ -576,7 +584,7 @@ public class PklPlugin implements Plugin<Project> {
|
||||
}
|
||||
|
||||
private <T extends BasePklTask> TaskProvider<T> createTask(Class<T> taskClass, BasePklSpec spec) {
|
||||
return project
|
||||
return project()
|
||||
.getTasks()
|
||||
.register(spec.getName(), taskClass, task -> configureBaseTask(task, spec));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@NonnullByDefault
|
||||
@NullMarked
|
||||
package org.pkl.gradle;
|
||||
|
||||
import org.pkl.core.util.NonnullByDefault;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@NonnullByDefault
|
||||
@NullMarked
|
||||
package org.pkl.gradle.spec;
|
||||
|
||||
import org.pkl.core.util.NonnullByDefault;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Transformer;
|
||||
import org.gradle.api.file.ConfigurableFileCollection;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
import org.gradle.api.model.ObjectFactory;
|
||||
@@ -45,10 +46,9 @@ import org.gradle.api.tasks.Optional;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.commons.cli.CliBaseOptions;
|
||||
import org.pkl.core.evaluatorSettings.Color;
|
||||
import org.pkl.core.util.LateInit;
|
||||
import org.pkl.core.util.Nullable;
|
||||
import org.pkl.gradle.utils.PluginUtils;
|
||||
|
||||
@CacheableTask
|
||||
@@ -86,27 +86,21 @@ public abstract class BasePklTask extends DefaultTask {
|
||||
@Optional
|
||||
@PathSensitive(PathSensitivity.ABSOLUTE)
|
||||
public Provider<File> getSettingsModuleFile() {
|
||||
//noinspection RedundantCast
|
||||
return getParsedSettingsModule()
|
||||
// NullAway needs this redundant cast
|
||||
.map(
|
||||
it -> {
|
||||
if (it instanceof File file) {
|
||||
return file;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
(Transformer<@Nullable File, Object>)
|
||||
object -> object instanceof File file ? file : null);
|
||||
}
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
public Provider<URI> getSettingsModuleUri() {
|
||||
//noinspection RedundantCast
|
||||
return getParsedSettingsModule()
|
||||
.map(
|
||||
it -> {
|
||||
if (it instanceof URI uri) {
|
||||
return uri;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
// NullAway needs this redundant cast
|
||||
.map((Transformer<@Nullable URI, Object>) object -> object instanceof URI uri ? uri : null);
|
||||
}
|
||||
|
||||
// Exposed as a task input via evalRootDirPath, because we only need to depend
|
||||
@@ -180,13 +174,13 @@ public abstract class BasePklTask extends DefaultTask {
|
||||
|
||||
protected abstract void doRunTask();
|
||||
|
||||
@LateInit protected CliBaseOptions cachedOptions;
|
||||
protected @Nullable CliBaseOptions __cachedOptions;
|
||||
|
||||
// Must be called during task execution time only.
|
||||
@Internal
|
||||
protected CliBaseOptions getCliBaseOptions() {
|
||||
if (cachedOptions == null) {
|
||||
cachedOptions =
|
||||
if (__cachedOptions == null) {
|
||||
__cachedOptions =
|
||||
new CliBaseOptions(
|
||||
getSourceModulesAsUris(),
|
||||
patternsFromStrings(getAllowedModules().get()),
|
||||
@@ -215,7 +209,7 @@ public abstract class BasePklTask extends DefaultTask {
|
||||
null,
|
||||
getPowerAssertions().getOrElse(false));
|
||||
}
|
||||
return cachedOptions;
|
||||
return __cachedOptions;
|
||||
}
|
||||
|
||||
@Internal
|
||||
|
||||
@@ -145,8 +145,8 @@ public abstract class ModulesTask extends BasePklTask {
|
||||
@Internal
|
||||
@Override
|
||||
protected CliBaseOptions getCliBaseOptions() {
|
||||
if (cachedOptions == null) {
|
||||
cachedOptions =
|
||||
if (__cachedOptions == null) {
|
||||
__cachedOptions =
|
||||
new CliBaseOptions(
|
||||
getSourceModulesAsUris(),
|
||||
patternsFromStrings(getAllowedModules().get()),
|
||||
@@ -175,6 +175,6 @@ public abstract class ModulesTask extends BasePklTask {
|
||||
null,
|
||||
getPowerAssertions().getOrElse(false));
|
||||
}
|
||||
return cachedOptions;
|
||||
return __cachedOptions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@NonnullByDefault
|
||||
@NullMarked
|
||||
package org.pkl.gradle.task;
|
||||
|
||||
import org.pkl.core.util.NonnullByDefault;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@NonnullByDefault
|
||||
@NullMarked
|
||||
package org.pkl.gradle.utils;
|
||||
|
||||
import org.pkl.core.util.NonnullByDefault;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
Reference in New Issue
Block a user