Skip to content

Commit

Permalink
Merge pull request #114 from gradle/tt/add-template-for-java-library
Browse files Browse the repository at this point in the history
Setup project generation for sample declarative java library and java application projects
  • Loading branch information
tresat authored Aug 13, 2024
2 parents 27a2583 + e1b5254 commit 9ea1f54
Show file tree
Hide file tree
Showing 21 changed files with 351 additions and 1 deletion.
Binary file modified unified-prototype/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion unified-prototype/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions-snapshots/gradle-8.10-20240719001820+0000-bin.zip
distributionUrl=https\://services.gradle.org/distributions-snapshots/gradle-8.11-20240812191308+0000-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
28 changes: 28 additions & 0 deletions unified-prototype/unified-plugin/plugin-common/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,16 +1,44 @@
plugins {
`kotlin-dsl`
id("build-logic.publishing")
groovy // For spock testing
}

description = "Common APIs and implementation classes shared by the ecosystem specific declarative prototypes"

dependencies {
implementation(libs.android.agp.application)

implementation("commons-io:commons-io:2.8.0")
implementation(gradleApi())
}

testing {
suites {
@Suppress("UnstableApiUsage")
val test by getting(JvmTestSuite::class) {
useSpock("2.2-groovy-3.0")

dependencies {
implementation("commons-io:commons-io:2.8.0")
}
}

@Suppress("UnstableApiUsage")
val integTest by registering(JvmTestSuite::class) {
useSpock("2.2-groovy-3.0")

dependencies {
implementation("commons-io:commons-io:2.8.0")
implementation(project(":plugin-jvm"))
implementation(project())
}
}

tasks.getByPath("check").dependsOn(integTest)
}
}

gradlePlugin {
plugins {
create("common") {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
javaLibrary {
javaVersion = 21

dependencies {
implementation(project(":java-util"))
implementation("com.google.guava:guava:32.1.3-jre")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.lib;

import com.google.common.collect.ImmutableList;

public class Library {
public Iterable<String> getMessages() {
// Verify that Guava is available
ImmutableList.Builder<String> builder = ImmutableList.builder();
builder.add("Hello from Java " + System.getProperty("java.version"));

return builder.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.gradle.util

import org.apache.commons.io.FileUtils
import spock.lang.Specification

class ResourceLoaderIntegrationTest extends Specification {
File outputDir

def setup() {
outputDir = new File("build/tmp/integTest/output").tap { deleteDir() }
}

def "can load resource from jar file"() {
given:
ResourceLoader resourceLoader = new ResourceLoader()

when:
resourceLoader.extractResourcesFromJar("templates/java-library", outputDir)

then:
assertOutputIs(['build.gradle.dcl', 'src/main/java/com/example/lib/Library.java'])
}

private void assertOutputIs(List<String> expectedRelativePaths) {
def actualPaths = FileUtils.listFiles(outputDir, null, true)*.path
def expectedPaths = expectedRelativePaths.collect { "${outputDir.toPath()}/$it".toString() }
assert actualPaths == expectedPaths
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.gradle.api.experimental.buildinit;

import org.gradle.api.file.Directory;
import org.gradle.buildinit.projectspecs.InitProjectConfig;
import org.gradle.buildinit.projectspecs.InitProjectGenerator;
import org.gradle.util.ResourceLoader;

/**
* An {@link InitProjectGenerator} that generates a project from a static template packaged
* as resources files in the {@link #TEMPLATES_ROOT} directory.
*/
@SuppressWarnings("UnstableApiUsage")
public final class StaticProjectGenerator implements InitProjectGenerator {
private static final String TEMPLATES_ROOT = "templates";

@Override
public void generate(InitProjectConfig config, Directory projectDir) {
if (!(config.getProjectSpec() instanceof StaticProjectSpec projectSpec)) {
throw new IllegalArgumentException("Unknown project type: " + config.getProjectSpec().getDisplayName() + " (" + config.getProjectSpec().getClass().getName() + ")");
}

String templatePath = TEMPLATES_ROOT + "/" + projectSpec.getTemplatePath();
ResourceLoader resourceLoader = new ResourceLoader();

try {
resourceLoader.extractResourcesFromJar(templatePath, projectDir.getAsFile());
} catch (Exception e) {
throw new RuntimeException("Error extracting resources for: '" + projectSpec.getDisplayName() + "' from: '" + templatePath + "'!", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.gradle.api.experimental.buildinit;

import org.gradle.buildinit.projectspecs.InitProjectParameter;
import org.gradle.buildinit.projectspecs.InitProjectSpec;

import java.util.Collections;
import java.util.List;

/**
* An {@link InitProjectSpec} that represents a project that can be generated from a static template
* using the {@link StaticProjectGenerator}
*/
@SuppressWarnings("UnstableApiUsage")
public final class StaticProjectSpec implements InitProjectSpec {
private final String templatePath;
private final String displayName;

public StaticProjectSpec(String templatePath, String displayName) {
this.templatePath = templatePath;
this.displayName = displayName;
}

@Override
public String getDisplayName() {
return displayName;
}

@Override
public List<InitProjectParameter<?>> getParameters() {
return Collections.emptyList();
}

public String getTemplatePath() {
return templatePath;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.gradle.util;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Iterator;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
* Static util class containing methods for loading resources from the classpath.
*/
public final class ResourceLoader {
/**
* Recursively extracts the contents of a directory in a jar file on the classpath to a specified directory.
*
* @param relativePath path to the source directory within the jar file
* @param destDir target directory to extract the contents to
* @throws IOException if an I/O error occurs
*/
public void extractResourcesFromJar(String relativePath, File destDir) throws IOException {
URL jarDirURL = ResourceLoader.class.getClassLoader().getResource(relativePath);
if (jarDirURL == null) {
throw new IllegalArgumentException("Directory: '" + relativePath + "' not found on classpath.");
}
JarFile jarFile = ((JarURLConnection) jarDirURL.openConnection()).getJarFile();
System.out.println("Using URL: " + jarDirURL);

Iterator<JarEntry> iterator = jarFile.entries().asIterator();
while (iterator.hasNext()) {
JarEntry entry = iterator.next();
String entryName = entry.getName();

if (entryName.startsWith(relativePath)) {
String entrySuffix = entryName.substring(relativePath.length());
System.out.println("Entry: " + entry + " name: " + entryName + " suffix: " + entrySuffix);
File destFile = new File(destDir, entrySuffix);
System.out.println("Dest file: " + destFile);

if (entry.isDirectory()) {
System.out.println("Is directory");
FileUtils.forceMkdir(destFile);
System.out.println("Created directory");
} else {
System.out.println("Is file");
try (InputStream is = jarFile.getInputStream(entry);
FileOutputStream fos = new FileOutputStream(destFile)) {
IOUtils.copy(is, fos);
}
System.out.println("Copied file");
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.gradle.util

import org.apache.commons.io.FileUtils

import spock.lang.Specification

class ResourceLoaderTest extends Specification {
def "can load resource from jar file"() {
given:
File output = new File("output").tap { mkdirs() }
ResourceLoader resourceLoader = new ResourceLoader(this.getClass().getClassLoader())

when:
File templatesDir = resourceLoader.getResource("templates/java-library")
FileUtils.copyDirectory(templatesDir, output)

then:
FileUtils.listFiles(output, null, true)*.path == ['output/build.gradle.dcl', 'output/src/main/java/com/example/lib/Library.java']
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
javaLibrary {
javaVersion = 21

dependencies {
implementation(project(":java-util"))
implementation("com.google.guava:guava:32.1.3-jre")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.lib;

import com.google.common.collect.ImmutableList;

public class Library {
public Iterable<String> getMessages() {
// Verify that Guava is available
ImmutableList.Builder<String> builder = ImmutableList.builder();
builder.add("Hello from Java " + System.getProperty("java.version"));

return builder.build();
}
}
2 changes: 2 additions & 0 deletions unified-prototype/unified-plugin/plugin-jvm/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ description = "Implements the declarative JVM DSL prototype"

dependencies {
implementation(project(":plugin-common"))
implementation("commons-io:commons-io:2.8.0")
implementation("org.gradle.toolchains:foojay-resolver:0.8.0")
implementation(gradleApi())
}

gradlePlugin {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.gradle.api.experimental.buildinit;

import java.util.Arrays;
import java.util.List;

import org.gradle.buildinit.projectspecs.InitProjectGenerator;
import org.gradle.buildinit.projectspecs.InitProjectSpec;
import org.gradle.buildinit.projectspecs.InitProjectSource;

/**
* A {@link InitProjectSource} of project specifications for JVM projects.
*/
@SuppressWarnings("UnstableApiUsage")
public final class JVMProjectSource implements InitProjectSource {
@Override
public List<InitProjectSpec> getProjectSpecs() {
return Arrays.asList(
new StaticProjectSpec("java-library", "Declarative Java Library Project"),
new StaticProjectSpec("java-application", "Declarative Java Application Project")
);
}

@Override
public InitProjectGenerator getProjectGenerator() {
return new StaticProjectGenerator();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@org.gradle.api.NonNullApi
package org.gradle.api.experimental.buildinit;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.gradle.api.experimental.buildinit.JVMProjectSource
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package templates.`java-application`

javaApplication {
// compile for 17
javaVersion = 17
mainClass = "com.example.App"

dependencies {
implementation(project(":java-util"))
implementation("com.google.guava:guava:32.1.3-jre")
}

testing {
// test on 21
testJavaVersion = 21

dependencies {
implementation("org.junit.jupiter:junit-jupiter:5.10.2")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.example;

import com.example.utils.Utils;
import com.google.common.collect.ImmutableList;

public class App {
public static void main(String[] args) {
// Verify that Guava is available
ImmutableList.Builder<String> builder = ImmutableList.builder();
builder.add("Hello from Java " + System.getProperty("java.version"));

// Verify that the Java library is available
Utils utils = new Utils();
builder.add(utils.getWelcome());

ImmutableList<String> messages = builder.build();
messages.forEach(System.out::println);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example;

import org.junit.jupiter.api.Test;

public class AppTest {
@Test
void appHasATest() {
assert true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
javaLibrary {
javaVersion = 21

dependencies {
implementation(project(":java-util"))
implementation("com.google.guava:guava:32.1.3-jre")
}
}
Loading

0 comments on commit 9ea1f54

Please sign in to comment.