From dbe0ac6e4ab8f2ca8484115507ce09b361f2040e Mon Sep 17 00:00:00 2001 From: Zaid Ajaj Date: Tue, 7 Jan 2025 00:40:23 +0100 Subject: [PATCH 1/8] Fix emitted import statements in generated such that imports with the same symbol are fully qualified --- CHANGELOG_PENDING.md | 2 + pkg/cmd/pulumi-language-java/language_test.go | 1 - .../Pulumi.yaml | 2 + .../pom.xml | 103 ++++++++++++ .../src/main/java/generated_program/App.java | 47 ++++++ .../sdks/fail_on_create-4.0.0/README.md | 1 + .../sdks/fail_on_create-4.0.0/build.gradle | 157 ++++++++++++++++++ .../sdks/fail_on_create-4.0.0/settings.gradle | 14 ++ .../com/pulumi/fail_on_create/Provider.java | 54 ++++++ .../pulumi/fail_on_create/ProviderArgs.java | 28 ++++ .../com/pulumi/fail_on_create/Resource.java | 78 +++++++++ .../pulumi/fail_on_create/ResourceArgs.java | 65 ++++++++ .../com/pulumi/fail_on_create/Utilities.java | 102 ++++++++++++ .../testdata/sdks/simple-2.0.0/build.gradle | 1 + pkg/codegen/java/gen_program.go | 59 +++++-- 15 files changed, 702 insertions(+), 12 deletions(-) create mode 100644 pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/Pulumi.yaml create mode 100644 pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/pom.xml create mode 100644 pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/src/main/java/generated_program/App.java create mode 100644 pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/README.md create mode 100644 pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/build.gradle create mode 100644 pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/settings.gradle create mode 100644 pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Provider.java create mode 100644 pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/ProviderArgs.java create mode 100644 pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Resource.java create mode 100644 pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/ResourceArgs.java create mode 100644 pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Utilities.java diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 2424a9c5fad..0abd3f3655f 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,3 +1,5 @@ ### Improvements + - Fix emitted import statements in generated such that imports with the same symbol are fully qualified + ### Bug Fixes \ No newline at end of file diff --git a/pkg/cmd/pulumi-language-java/language_test.go b/pkg/cmd/pulumi-language-java/language_test.go index eeed5ea7e6b..4a5ecde152c 100644 --- a/pkg/cmd/pulumi-language-java/language_test.go +++ b/pkg/cmd/pulumi-language-java/language_test.go @@ -163,7 +163,6 @@ var expectedFailures = map[string]string{ "l1-output-array": "#1560 Empty array literals are not generated correctly", "l1-output-map": "#1561 Map literals are not generated correctly", "l1-output-string": "#1562 Large string literals are not generated correctly", - "l2-failed-create-continue-on-error": "#1558 Duplicate identifiers aren't fully qualified", "l2-invoke-dependencies": "#1563 Invoke argument and result handling", "l2-invoke-options": "#1563 Invoke argument and result handling", "l2-invoke-options-depends-on": "#1563 Invoke argument and result handling", diff --git a/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/Pulumi.yaml b/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/Pulumi.yaml new file mode 100644 index 00000000000..c1f82ca12aa --- /dev/null +++ b/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/Pulumi.yaml @@ -0,0 +1,2 @@ +name: l2-failed-create-continue-on-error +runtime: java diff --git a/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/pom.xml b/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/pom.xml new file mode 100644 index 00000000000..a50390627ef --- /dev/null +++ b/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/pom.xml @@ -0,0 +1,103 @@ + + + 4.0.0 + + com.pulumi + l2-failed-create-continue-on-error + 1.0-SNAPSHOT + + + UTF-8 + 11 + 11 + 11 + generated_program.App + + + + + + repository-0 + REPOSITORY + + + + + + com.pulumi + pulumi + CORE.VERSION + + + com.pulumi + fail_on_create + 4.0.0 + + com.pulumi + simple + 2.0.0 + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.2.2 + + + + true + ${mainClass} + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.4.2 + + + + true + ${mainClass} + + + + jar-with-dependencies + + + + + make-my-jar-with-dependencies + package + + single + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + ${mainClass} + ${mainArgs} + + + + org.apache.maven.plugins + maven-wrapper-plugin + 3.1.1 + + 3.8.5 + + + + + \ No newline at end of file diff --git a/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/src/main/java/generated_program/App.java b/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/src/main/java/generated_program/App.java new file mode 100644 index 00000000000..bff816315a1 --- /dev/null +++ b/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/src/main/java/generated_program/App.java @@ -0,0 +1,47 @@ +package generated_program; + +import com.pulumi.Context; +import com.pulumi.Pulumi; +import com.pulumi.core.Output; +import com.pulumi.resources.CustomResourceOptions; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class App { + public static void main(String[] args) { + Pulumi.run(App::stack); + } + + public static void stack(Context ctx) { + var failing = new com.pulumi.fail_on_create.Resource("failing", com.pulumi.fail_on_create.ResourceArgs.builder() + .value(false) + .build()); + + var dependent = new com.pulumi.simple.Resource("dependent", com.pulumi.simple.ResourceArgs.builder() + .value(true) + .build(), CustomResourceOptions.builder() + .dependsOn(failing) + .build()); + + var dependent_on_output = new com.pulumi.simple.Resource("dependent_on_output", com.pulumi.simple.ResourceArgs.builder() + .value(failing.value()) + .build()); + + var independent = new com.pulumi.simple.Resource("independent", com.pulumi.simple.ResourceArgs.builder() + .value(true) + .build()); + + var double_dependency = new com.pulumi.simple.Resource("double_dependency", com.pulumi.simple.ResourceArgs.builder() + .value(true) + .build(), CustomResourceOptions.builder() + .dependsOn( + independent, + dependent_on_output) + .build()); + + } +} diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/README.md b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/README.md new file mode 100644 index 00000000000..8d1c8b69c3f --- /dev/null +++ b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/README.md @@ -0,0 +1 @@ + diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/build.gradle b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/build.gradle new file mode 100644 index 00000000000..2a2d880b018 --- /dev/null +++ b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/build.gradle @@ -0,0 +1,157 @@ +// *** WARNING: this file was generated by pulumi-java-gen *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +plugins { + id("signing") + id("java-library") + id("maven-publish") +} + +group = "com.pulumi" + +def resolvedVersion = System.getenv("PACKAGE_VERSION") ?: + (project.version == "unspecified" + ? "4.0.0" + : project.version) + +def signingKey = System.getenv("SIGNING_KEY") +def signingPassword = System.getenv("SIGNING_PASSWORD") +def publishRepoURL = System.getenv("PUBLISH_REPO_URL") +def publishRepoUsername = System.getenv("PUBLISH_REPO_USERNAME") +def publishRepoPassword = System.getenv("PUBLISH_REPO_PASSWORD") + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + +compileJava { + options.fork = true + options.forkOptions.jvmArgs.addAll(["-Xmx16g"]) + options.encoding = "UTF-8" +} + +repositories { + maven { + url("REPOSITORY") + } + mavenLocal() + maven { // The google mirror is less flaky than mavenCentral() + url("https://maven-central.storage-download.googleapis.com/maven2/") + } + mavenCentral() +} + +dependencies { + implementation("com.google.code.findbugs:jsr305:3.0.2") + implementation("com.google.code.gson:gson:2.8.9") + implementation("com.pulumi:pulumi:CORE.VERSION") +} + +task sourcesJar(type: Jar) { + from sourceSets.main.allJava + archiveClassifier.set('sources') +} + +task javadocJar(type: Jar) { + from javadoc + archiveClassifier.set('javadoc') + zip64 = true +} + +def genPulumiResources = tasks.register('genPulumiResources') { + doLast { + def resourcesDir = sourceSets.main.output.resourcesDir + def subDir = project.name.replace(".", "/") + def outDir = file("$resourcesDir/$subDir") + outDir.mkdirs() + new File(outDir, "version.txt").text = resolvedVersion + def builder = new groovy.json.JsonBuilder() + builder { + resource true + name "fail_on_create" + version resolvedVersion + } + def infoJson = builder.toPrettyString() + new File(outDir, "plugin.json").text = infoJson + } +} + +jar.configure { + dependsOn genPulumiResources +} + +publishing { + publications { + mainPublication(MavenPublication) { + groupId = "com.pulumi" + artifactId = "fail_on_create" + version = resolvedVersion + from components.java + artifact sourcesJar + artifact javadocJar + + pom { + inceptionYear = "" + name = "" + packaging = "jar" + description = " " + + url = "https://example.com" + + scm { + connection = "https://example.com" + developerConnection = "https://example.com" + url = "https://example.com" + } + + licenses { + license { + name = "" + url = "" + } + } + + developers { + developer { + id = "" + name = "" + email = "" + } + } + } + } + } + + if (publishRepoURL) { + repositories { + maven { + name = "PublishRepo" + url = publishRepoURL + credentials { + username = publishRepoUsername + password = publishRepoPassword + } + } + } + } +} + +javadoc { + if (JavaVersion.current().isJava9Compatible()) { + options.addBooleanOption('html5', true) + } + options.jFlags("-Xmx8g", "-Xms512m") +} + +jar { + zip64 = true +} + +if (signingKey) { + signing { + useInMemoryPgpKeys(signingKey, signingPassword) + sign publishing.publications.mainPublication + } +} \ No newline at end of file diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/settings.gradle b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/settings.gradle new file mode 100644 index 00000000000..6f46da2d1a7 --- /dev/null +++ b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/settings.gradle @@ -0,0 +1,14 @@ +// *** WARNING: this file was generated by pulumi-java-gen. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +pluginManagement { + repositories { + maven { // The google mirror is less flaky than mavenCentral() + url("https://maven-central.storage-download.googleapis.com/maven2/") + } + gradlePluginPortal() + } +} + +rootProject.name = "com.pulumi.fail_on_create" +include("lib") diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Provider.java b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Provider.java new file mode 100644 index 00000000000..513b5af421f --- /dev/null +++ b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Provider.java @@ -0,0 +1,54 @@ +// *** WARNING: this file was generated by pulumi-language-java. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +package com.pulumi.fail_on_create; + +import com.pulumi.core.Output; +import com.pulumi.core.annotations.ResourceType; +import com.pulumi.core.internal.Codegen; +import com.pulumi.fail_on_create.ProviderArgs; +import com.pulumi.fail_on_create.Utilities; +import javax.annotation.Nullable; + +@ResourceType(type="pulumi:providers:fail_on_create") +public class Provider extends com.pulumi.resources.ProviderResource { + /** + * + * @param name The _unique_ name of the resulting resource. + */ + public Provider(java.lang.String name) { + this(name, ProviderArgs.Empty); + } + /** + * + * @param name The _unique_ name of the resulting resource. + * @param args The arguments to use to populate this resource's properties. + */ + public Provider(java.lang.String name, @Nullable ProviderArgs args) { + this(name, args, null); + } + /** + * + * @param name The _unique_ name of the resulting resource. + * @param args The arguments to use to populate this resource's properties. + * @param options A bag of options that control this resource's behavior. + */ + public Provider(java.lang.String name, @Nullable ProviderArgs args, @Nullable com.pulumi.resources.CustomResourceOptions options) { + super("fail_on_create", name, makeArgs(args, options), makeResourceOptions(options, Codegen.empty()), false); + } + + private static ProviderArgs makeArgs(@Nullable ProviderArgs args, @Nullable com.pulumi.resources.CustomResourceOptions options) { + if (options != null && options.getUrn().isPresent()) { + return null; + } + return args == null ? ProviderArgs.Empty : args; + } + + private static com.pulumi.resources.CustomResourceOptions makeResourceOptions(@Nullable com.pulumi.resources.CustomResourceOptions options, @Nullable Output id) { + var defaultOptions = com.pulumi.resources.CustomResourceOptions.builder() + .version(Utilities.getVersion()) + .build(); + return com.pulumi.resources.CustomResourceOptions.merge(defaultOptions, options, id); + } + +} diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/ProviderArgs.java b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/ProviderArgs.java new file mode 100644 index 00000000000..c6323b23a12 --- /dev/null +++ b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/ProviderArgs.java @@ -0,0 +1,28 @@ +// *** WARNING: this file was generated by pulumi-language-java. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +package com.pulumi.fail_on_create; + + + + +public final class ProviderArgs extends com.pulumi.resources.ResourceArgs { + + public static final ProviderArgs Empty = new ProviderArgs(); + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private ProviderArgs $; + + public Builder() { + $ = new ProviderArgs(); + } + public ProviderArgs build() { + return $; + } + } + +} diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Resource.java b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Resource.java new file mode 100644 index 00000000000..416e9e088d7 --- /dev/null +++ b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Resource.java @@ -0,0 +1,78 @@ +// *** WARNING: this file was generated by pulumi-language-java. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +package com.pulumi.fail_on_create; + +import com.pulumi.core.Output; +import com.pulumi.core.annotations.Export; +import com.pulumi.core.annotations.ResourceType; +import com.pulumi.core.internal.Codegen; +import com.pulumi.fail_on_create.ResourceArgs; +import com.pulumi.fail_on_create.Utilities; +import java.lang.Boolean; +import javax.annotation.Nullable; + +@ResourceType(type="fail_on_create:index:Resource") +public class Resource extends com.pulumi.resources.CustomResource { + @Export(name="value", refs={Boolean.class}, tree="[0]") + private Output value; + + public Output value() { + return this.value; + } + + /** + * + * @param name The _unique_ name of the resulting resource. + */ + public Resource(java.lang.String name) { + this(name, ResourceArgs.Empty); + } + /** + * + * @param name The _unique_ name of the resulting resource. + * @param args The arguments to use to populate this resource's properties. + */ + public Resource(java.lang.String name, ResourceArgs args) { + this(name, args, null); + } + /** + * + * @param name The _unique_ name of the resulting resource. + * @param args The arguments to use to populate this resource's properties. + * @param options A bag of options that control this resource's behavior. + */ + public Resource(java.lang.String name, ResourceArgs args, @Nullable com.pulumi.resources.CustomResourceOptions options) { + super("fail_on_create:index:Resource", name, makeArgs(args, options), makeResourceOptions(options, Codegen.empty()), false); + } + + private Resource(java.lang.String name, Output id, @Nullable com.pulumi.resources.CustomResourceOptions options) { + super("fail_on_create:index:Resource", name, null, makeResourceOptions(options, id), false); + } + + private static ResourceArgs makeArgs(ResourceArgs args, @Nullable com.pulumi.resources.CustomResourceOptions options) { + if (options != null && options.getUrn().isPresent()) { + return null; + } + return args == null ? ResourceArgs.Empty : args; + } + + private static com.pulumi.resources.CustomResourceOptions makeResourceOptions(@Nullable com.pulumi.resources.CustomResourceOptions options, @Nullable Output id) { + var defaultOptions = com.pulumi.resources.CustomResourceOptions.builder() + .version(Utilities.getVersion()) + .build(); + return com.pulumi.resources.CustomResourceOptions.merge(defaultOptions, options, id); + } + + /** + * Get an existing Host resource's state with the given name, ID, and optional extra + * properties used to qualify the lookup. + * + * @param name The _unique_ name of the resulting resource. + * @param id The _unique_ provider ID of the resource to lookup. + * @param options Optional settings to control the behavior of the CustomResource. + */ + public static Resource get(java.lang.String name, Output id, @Nullable com.pulumi.resources.CustomResourceOptions options) { + return new Resource(name, id, options); + } +} diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/ResourceArgs.java b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/ResourceArgs.java new file mode 100644 index 00000000000..505a2e28f4d --- /dev/null +++ b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/ResourceArgs.java @@ -0,0 +1,65 @@ +// *** WARNING: this file was generated by pulumi-language-java. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +package com.pulumi.fail_on_create; + +import com.pulumi.core.Output; +import com.pulumi.core.annotations.Import; +import com.pulumi.exceptions.MissingRequiredPropertyException; +import java.lang.Boolean; +import java.util.Objects; + + +public final class ResourceArgs extends com.pulumi.resources.ResourceArgs { + + public static final ResourceArgs Empty = new ResourceArgs(); + + @Import(name="value", required=true) + private Output value; + + public Output value() { + return this.value; + } + + private ResourceArgs() {} + + private ResourceArgs(ResourceArgs $) { + this.value = $.value; + } + + public static Builder builder() { + return new Builder(); + } + public static Builder builder(ResourceArgs defaults) { + return new Builder(defaults); + } + + public static final class Builder { + private ResourceArgs $; + + public Builder() { + $ = new ResourceArgs(); + } + + public Builder(ResourceArgs defaults) { + $ = new ResourceArgs(Objects.requireNonNull(defaults)); + } + + public Builder value(Output value) { + $.value = value; + return this; + } + + public Builder value(Boolean value) { + return value(Output.of(value)); + } + + public ResourceArgs build() { + if ($.value == null) { + throw new MissingRequiredPropertyException("ResourceArgs", "value"); + } + return $; + } + } + +} diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Utilities.java b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Utilities.java new file mode 100644 index 00000000000..5002cbcb0ff --- /dev/null +++ b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Utilities.java @@ -0,0 +1,102 @@ +// *** WARNING: this file was generated by pulumi-language-java. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +package com.pulumi.fail_on_create; + + + + + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.Optional; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import com.pulumi.core.internal.Environment; +import com.pulumi.deployment.InvokeOptions; +import com.pulumi.deployment.InvokeOutputOptions; + +public class Utilities { + + public static Optional getEnv(java.lang.String... names) { + for (var n : names) { + var value = Environment.getEnvironmentVariable(n); + if (value.isValue()) { + return Optional.of(value.value()); + } + } + return Optional.empty(); + } + + public static Optional getEnvBoolean(java.lang.String... names) { + for (var n : names) { + var value = Environment.getBooleanEnvironmentVariable(n); + if (value.isValue()) { + return Optional.of(value.value()); + } + } + return Optional.empty(); + } + + public static Optional getEnvInteger(java.lang.String... names) { + for (var n : names) { + var value = Environment.getIntegerEnvironmentVariable(n); + if (value.isValue()) { + return Optional.of(value.value()); + } + } + return Optional.empty(); + } + + public static Optional getEnvDouble(java.lang.String... names) { + for (var n : names) { + var value = Environment.getDoubleEnvironmentVariable(n); + if (value.isValue()) { + return Optional.of(value.value()); + } + } + return Optional.empty(); + } + + public static InvokeOptions withVersion(@Nullable InvokeOptions options) { + if (options != null && options.getVersion().isPresent()) { + return options; + } + return new InvokeOptions( + options == null ? null : options.getParent().orElse(null), + options == null ? null : options.getProvider().orElse(null), + getVersion() + ); + } + + public static InvokeOutputOptions withVersion(@Nullable InvokeOutputOptions options) { + if (options != null && options.getVersion().isPresent()) { + return options; + } + return new InvokeOutputOptions( + options == null ? null : options.getParent().orElse(null), + options == null ? null : options.getProvider().orElse(null), + getVersion(), + options == null ? null : options.getDependsOn() + ); + } + + private static final java.lang.String version; + public static java.lang.String getVersion() { + return version; + } + + static { + var resourceName = "com/pulumi/fail_on_create/version.txt"; + var versionFile = Utilities.class.getClassLoader().getResourceAsStream(resourceName); + if (versionFile == null) { + throw new IllegalStateException( + java.lang.String.format("expected resource '%s' on Classpath, not found", resourceName) + ); + } + version = new BufferedReader(new InputStreamReader(versionFile)) + .lines() + .collect(Collectors.joining("\n")) + .trim(); + } +} diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/simple-2.0.0/build.gradle b/pkg/cmd/pulumi-language-java/testdata/sdks/simple-2.0.0/build.gradle index f48b100bd18..93c6f71c353 100644 --- a/pkg/cmd/pulumi-language-java/testdata/sdks/simple-2.0.0/build.gradle +++ b/pkg/cmd/pulumi-language-java/testdata/sdks/simple-2.0.0/build.gradle @@ -46,6 +46,7 @@ repositories { dependencies { implementation("com.google.code.findbugs:jsr305:3.0.2") implementation("com.google.code.gson:gson:2.8.9") + implementation("com.pulumi:fail_on_create:4.0.0") implementation("com.pulumi:pulumi:CORE.VERSION") } diff --git a/pkg/codegen/java/gen_program.go b/pkg/codegen/java/gen_program.go index fb181577fcd..f8173bca3e0 100644 --- a/pkg/codegen/java/gen_program.go +++ b/pkg/codegen/java/gen_program.go @@ -49,7 +49,8 @@ type generator struct { // // later on when apply a traversal sush as resourceGroup.getName(), // we should rewrite it as resourceGroup.thenApply(getResourceGroupResult -> getResourceGroupResult.getName()) - functionInvokes map[string]*schema.Function + functionInvokes map[string]*schema.Function + importStatements []string } // genComment generates a comment into the output. @@ -583,14 +584,14 @@ func reduceInputTypeFromArray(arrayType *schema.ArrayType) *schema.ArrayType { } } -func collectResourceImports(resource *pcl.Resource) []string { +func (g *generator) collectResourceImports(resource *pcl.Resource) []string { imports := make([]string, 0) pkg, module, name, _ := resource.DecomposeToken() resourceImport := pulumiImport(pkg, module, name) imports = append(imports, resourceImport) if len(resource.Inputs) > 0 || hasCustomResourceOptions(resource) { // import args type name - argsTypeName := resourceArgsTypeName(resource) + argsTypeName := g.resourceArgsTypeName(resource) resourceArgsImport := pulumiImport(pkg, module, argsTypeName) imports = append(imports, resourceArgsImport) resourceProperties := typedResourceProperties(resource) @@ -716,7 +717,7 @@ func (g *generator) collectImports(nodes []pcl.Node) []string { case *pcl.Resource: // collect resource imports resource := node - imports = append(imports, collectResourceImports(resource)...) + imports = append(imports, g.collectResourceImports(resource)...) for _, prop := range resource.Inputs { _, diags := model.VisitExpression(prop.Value, model.IdentityVisitor, visitFunctionCalls) g.diagnostics = append(g.diagnostics, diags...) @@ -745,17 +746,45 @@ func (g *generator) genPreamble(w io.Writer, nodes []pcl.Node) { g.genImport(w, "com.pulumi.Context") g.genImport(w, "com.pulumi.Pulumi") g.genImport(w, "com.pulumi.core.Output") + + importStatements := g.collectImports(nodes) + + importMember := func(importDef string) string { + return importDef[strings.LastIndex(importDef, ".")+1:] + } + + // if we two imports such as the following: + // - com.pulumi.my_package.ResourceType + // - com.pulumi.your_package.ResourceType + // then we shouldn't generate import statements for them + // instead, we use the fully qualified name at the usage site + importsWithDuplicateName := codegen.NewStringSet() + for _, importDefA := range importStatements { + for _, importDefB := range importStatements { + if importMember(importDefA) == importMember(importDefB) && importDefA != importDefB { + importsWithDuplicateName.Add(importDefA) + importsWithDuplicateName.Add(importDefB) + } + } + } + // Write out the specific imports from used nodes - for _, importDef := range g.collectImports(nodes) { + for _, importDef := range importStatements { if strings.HasSuffix(importDef, ".String") { // A type named `String` is being imported so we need to fully qualify our // use of the built-in `java.lang.String` type to avoid the conflict. javaStringType = "java.lang.String" } - g.genImport(w, importDef) + if !importsWithDuplicateName.Has(importDef) { + // do not generate import statements for symbols with duplicate members + // because those will require full qualification at the usage site + g.genImport(w, importDef) + } } + g.importStatements = importStatements + if containsFunctionCall("toJSON", nodes) { // import static functions from the Serialization class g.Fgen(w, "import static com.pulumi.codegen.internal.Serialization.*;\n") @@ -817,7 +846,7 @@ func (g *generator) genPostamble(w io.Writer, _ []pcl.Node) { } // resourceTypeName computes the Java resource class name for the given resource. -func resourceTypeName(resource *pcl.Resource) string { +func (g *generator) resourceTypeName(resource *pcl.Resource) string { // Compute the resource type from the Pulumi type token. pkg, module, member, diags := resource.DecomposeToken() contract.Assertf(len(diags) == 0, "failed to decompose resource token: %v", diags) @@ -825,12 +854,20 @@ func resourceTypeName(resource *pcl.Resource) string { member = "Provider" } + for _, importStatement := range g.importStatements { + // if there is an import statement that ends with the same member name + // return the fully qualified resource name + if strings.HasSuffix(importStatement, "."+member) && pulumiImport(pkg, module, member) != importStatement { + return pulumiImport(pkg, module, member) + } + } + return names.Title(member) } // resourceArgsTypeName computes the Java arguments class name for the given resource. -func resourceArgsTypeName(r *pcl.Resource) string { - return fmt.Sprintf("%sArgs", resourceTypeName(r)) +func (g *generator) resourceArgsTypeName(r *pcl.Resource) string { + return fmt.Sprintf("%sArgs", g.resourceTypeName(r)) } // Returns the expression that should be emitted for a resource's "name" parameter given its base name @@ -987,8 +1024,8 @@ func typedResourceProperties(resource *pcl.Resource) map[string]schema.Type { } func (g *generator) genResource(w io.Writer, resource *pcl.Resource) { - resourceTypeName := resourceTypeName(resource) - resourceArgs := resourceArgsTypeName(resource) + resourceTypeName := g.resourceTypeName(resource) + resourceArgs := g.resourceArgsTypeName(resource) variableName := names.LowerCamelCase(names.MakeValidIdentifier(resource.Name())) g.genTrivia(w, resource.Definition.Tokens.GetType("")) for _, l := range resource.Definition.Tokens.GetLabels(nil) { From 42954e5fdd1191202a393c9418155ef7bc967c48 Mon Sep 17 00:00:00 2001 From: Zaid Ajaj Date: Tue, 7 Jan 2025 00:41:50 +0100 Subject: [PATCH 2/8] typo --- CHANGELOG_PENDING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 0abd3f3655f..02344c8c465 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,5 +1,5 @@ ### Improvements - - Fix emitted import statements in generated such that imports with the same symbol are fully qualified + - Fix emitted import statements in generated programs such that imports with the same symbol are fully qualified ### Bug Fixes \ No newline at end of file From b1f80a8da03062db046610e1d0ab6a708e297119 Mon Sep 17 00:00:00 2001 From: Zaid Ajaj Date: Tue, 7 Jan 2025 00:43:26 +0100 Subject: [PATCH 3/8] typo in comment --- pkg/codegen/java/gen_program.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/codegen/java/gen_program.go b/pkg/codegen/java/gen_program.go index f8173bca3e0..8e5ee3cef9f 100644 --- a/pkg/codegen/java/gen_program.go +++ b/pkg/codegen/java/gen_program.go @@ -753,7 +753,7 @@ func (g *generator) genPreamble(w io.Writer, nodes []pcl.Node) { return importDef[strings.LastIndex(importDef, ".")+1:] } - // if we two imports such as the following: + // if we have two imports such as the following: // - com.pulumi.my_package.ResourceType // - com.pulumi.your_package.ResourceType // then we shouldn't generate import statements for them From 6e7a28bbf6736cf7e28543f794f5d836427c47e9 Mon Sep 17 00:00:00 2001 From: Zaid Ajaj Date: Tue, 7 Jan 2025 01:13:48 +0100 Subject: [PATCH 4/8] Do not emit local dependencies other than the core pulumi sdk --- .../testdata/sdks/simple-2.0.0/build.gradle | 1 - pkg/codegen/java/gen.go | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/simple-2.0.0/build.gradle b/pkg/cmd/pulumi-language-java/testdata/sdks/simple-2.0.0/build.gradle index 93c6f71c353..f48b100bd18 100644 --- a/pkg/cmd/pulumi-language-java/testdata/sdks/simple-2.0.0/build.gradle +++ b/pkg/cmd/pulumi-language-java/testdata/sdks/simple-2.0.0/build.gradle @@ -46,7 +46,6 @@ repositories { dependencies { implementation("com.google.code.findbugs:jsr305:3.0.2") implementation("com.google.code.gson:gson:2.8.9") - implementation("com.pulumi:fail_on_create:4.0.0") implementation("com.pulumi:pulumi:CORE.VERSION") } diff --git a/pkg/codegen/java/gen.go b/pkg/codegen/java/gen.go index bb804b4a8f0..37dee0cb52a 100644 --- a/pkg/codegen/java/gen.go +++ b/pkg/codegen/java/gen.go @@ -2270,6 +2270,13 @@ func GeneratePackage( ) } + // local dependencies should only be using the pulumi core sdk + // in the future we will be adding local depedencies to component providers + // but for non-component providers, we should only be using the pulumi core sdk + if parts[0] != "com.pulumi" || parts[1] != "pulumi" { + continue + } + k := parts[0] + ":" + parts[1] dependencies[k] = parts[2] From 3e461f5a6e4aefb067064a9ed0f3bf74e8c473ba Mon Sep 17 00:00:00 2001 From: Zaid Ajaj Date: Tue, 7 Jan 2025 01:14:51 +0100 Subject: [PATCH 5/8] simplify --- pkg/codegen/java/gen.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/codegen/java/gen.go b/pkg/codegen/java/gen.go index 37dee0cb52a..8fe508132e1 100644 --- a/pkg/codegen/java/gen.go +++ b/pkg/codegen/java/gen.go @@ -2273,7 +2273,7 @@ func GeneratePackage( // local dependencies should only be using the pulumi core sdk // in the future we will be adding local depedencies to component providers // but for non-component providers, we should only be using the pulumi core sdk - if parts[0] != "com.pulumi" || parts[1] != "pulumi" { + if parts[1] != "pulumi" { continue } From 3e3166600c3e49fbed0f1045dc57f76a48d56901 Mon Sep 17 00:00:00 2001 From: Zaid Ajaj Date: Tue, 7 Jan 2025 02:16:37 +0100 Subject: [PATCH 6/8] lint --- pkg/codegen/java/gen.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/codegen/java/gen.go b/pkg/codegen/java/gen.go index 8fe508132e1..b3b253d7968 100644 --- a/pkg/codegen/java/gen.go +++ b/pkg/codegen/java/gen.go @@ -2271,7 +2271,7 @@ func GeneratePackage( } // local dependencies should only be using the pulumi core sdk - // in the future we will be adding local depedencies to component providers + // in the future we will be adding local dependencies to component providers // but for non-component providers, we should only be using the pulumi core sdk if parts[1] != "pulumi" { continue From ec2807b36f6ced47def31845e9b57fdea8033208 Mon Sep 17 00:00:00 2001 From: Zaid Ajaj Date: Tue, 7 Jan 2025 04:44:46 +0100 Subject: [PATCH 7/8] optimize searching imports --- pkg/codegen/java/gen_program.go | 56 +++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/pkg/codegen/java/gen_program.go b/pkg/codegen/java/gen_program.go index 8e5ee3cef9f..6ebcdd8c5e0 100644 --- a/pkg/codegen/java/gen_program.go +++ b/pkg/codegen/java/gen_program.go @@ -49,8 +49,8 @@ type generator struct { // // later on when apply a traversal sush as resourceGroup.getName(), // we should rewrite it as resourceGroup.thenApply(getResourceGroupResult -> getResourceGroupResult.getName()) - functionInvokes map[string]*schema.Function - importStatements []string + functionInvokes map[string]*schema.Function + emittedTypeImportSymbols codegen.StringSet } // genComment generates a comment into the output. @@ -188,8 +188,9 @@ func GenerateProgram(program *pcl.Program) (map[string][]byte, hcl.Diagnostics, } g := &generator{ - program: program, - functionInvokes: map[string]*schema.Function{}, + program: program, + functionInvokes: map[string]*schema.Function{}, + emittedTypeImportSymbols: codegen.NewStringSet(), } g.Formatter = format.NewFormatter(g) @@ -747,43 +748,59 @@ func (g *generator) genPreamble(w io.Writer, nodes []pcl.Node) { g.genImport(w, "com.pulumi.Pulumi") g.genImport(w, "com.pulumi.core.Output") - importStatements := g.collectImports(nodes) + imports := g.collectImports(nodes) importMember := func(importDef string) string { return importDef[strings.LastIndex(importDef, ".")+1:] } + // a map to keep track of member names of imports + // e.g. ResourceType -> [ + // com.pulumi.my_package.ResourceType + // com.pulumi.your_package.ResourceType + // ] + importsByMember := map[string]codegen.StringSet{} + for _, importDef := range imports { + importMember := importMember(importDef) + if _, ok := importsByMember[importMember]; !ok { + // initialize the set + importsByMember[importMember] = codegen.NewStringSet() + } + + importsByMember[importMember].Add(importDef) + } + // if we have two imports such as the following: // - com.pulumi.my_package.ResourceType // - com.pulumi.your_package.ResourceType // then we shouldn't generate import statements for them // instead, we use the fully qualified name at the usage site importsWithDuplicateName := codegen.NewStringSet() - for _, importDefA := range importStatements { - for _, importDefB := range importStatements { - if importMember(importDefA) == importMember(importDefB) && importDefA != importDefB { - importsWithDuplicateName.Add(importDefA) - importsWithDuplicateName.Add(importDefB) - } + emittedTypeImportSymbols := codegen.NewStringSet() + for _, importDef := range imports { + if len(importsByMember[importMember(importDef)]) > 1 { + importsWithDuplicateName.Add(importDef) + } else { + emittedTypeImportSymbols.Add(importDef) } } // Write out the specific imports from used nodes - for _, importDef := range importStatements { + for _, importDef := range imports { if strings.HasSuffix(importDef, ".String") { // A type named `String` is being imported so we need to fully qualify our // use of the built-in `java.lang.String` type to avoid the conflict. javaStringType = "java.lang.String" } - if !importsWithDuplicateName.Has(importDef) { + if emittedTypeImportSymbols.Has(importDef) { // do not generate import statements for symbols with duplicate members // because those will require full qualification at the usage site g.genImport(w, importDef) } } - g.importStatements = importStatements + g.emittedTypeImportSymbols = emittedTypeImportSymbols if containsFunctionCall("toJSON", nodes) { // import static functions from the Serialization class @@ -854,12 +871,11 @@ func (g *generator) resourceTypeName(resource *pcl.Resource) string { member = "Provider" } - for _, importStatement := range g.importStatements { - // if there is an import statement that ends with the same member name - // return the fully qualified resource name - if strings.HasSuffix(importStatement, "."+member) && pulumiImport(pkg, module, member) != importStatement { - return pulumiImport(pkg, module, member) - } + if !g.emittedTypeImportSymbols.Has(pulumiImport(pkg, module, member)) { + // if we didn't emit an import statement for this symbol + // it means that there was a duplicate member name in the imports + // so return the fully qualified resource name + return pulumiImport(pkg, module, member) } return names.Title(member) From 60cb95e47f8368bffaf8374f5a4bf2b46615c760 Mon Sep 17 00:00:00 2001 From: Zaid Ajaj Date: Tue, 7 Jan 2025 05:05:35 +0100 Subject: [PATCH 8/8] ensure resource args type imports are not double qualified --- pkg/codegen/java/gen_program.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/codegen/java/gen_program.go b/pkg/codegen/java/gen_program.go index 6ebcdd8c5e0..795e1c22c73 100644 --- a/pkg/codegen/java/gen_program.go +++ b/pkg/codegen/java/gen_program.go @@ -593,8 +593,13 @@ func (g *generator) collectResourceImports(resource *pcl.Resource) []string { if len(resource.Inputs) > 0 || hasCustomResourceOptions(resource) { // import args type name argsTypeName := g.resourceArgsTypeName(resource) - resourceArgsImport := pulumiImport(pkg, module, argsTypeName) - imports = append(imports, resourceArgsImport) + alreadyFullyQualified := strings.Contains(argsTypeName, ".") + if !alreadyFullyQualified { + resourceArgsImport := pulumiImport(pkg, module, argsTypeName) + imports = append(imports, resourceArgsImport) + } else { + imports = append(imports, argsTypeName) + } resourceProperties := typedResourceProperties(resource) for _, inputProperty := range resource.Inputs { inputType := resourceProperties[inputProperty.Name]