Skip to content

Commit

Permalink
[WIP]
Browse files Browse the repository at this point in the history
  • Loading branch information
dmikurube committed Nov 30, 2023
1 parent 7dde39a commit de00c83
Show file tree
Hide file tree
Showing 7 changed files with 405 additions and 32 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ gradlePlugin {
id = "org.embulk.runset"
displayName = "A Gradle plugin to prepare an environment for running Embulk"
description = "${project.description}"
implementationClass = "org.embulk.gradle.runset.RunsetPlugin"
implementationClass = "org.embulk.gradle.runset.EmbulkRunSetPlugin"
tags = ["embulk"]
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@
public class EmbulkRunSetPlugin implements Plugin<Project> {
@Override
public void apply(final Project project) {
project.getTasks().register("installEmbulkRunSet", InstallEmbulkRunSet.class);
}
}
201 changes: 201 additions & 0 deletions src/main/java/org/embulk/gradle/runset/InstallEmbulkRunSet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/*
* Copyright 2022 The Embulk project
*
* 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
*
* http://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.embulk.gradle.runset;

import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.gradle.api.IllegalDependencyNotation;
import org.gradle.api.Project;
import org.gradle.api.artifacts.ArtifactCollection;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.ResolvableDependencies;
import org.gradle.api.artifacts.component.ComponentIdentifier;
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
import org.gradle.api.artifacts.result.ArtifactResolutionResult;
import org.gradle.api.artifacts.result.ArtifactResult;
import org.gradle.api.artifacts.result.ComponentArtifactsResult;
import org.gradle.api.artifacts.result.ResolvedArtifactResult;
import org.gradle.api.file.DuplicatesStrategy;
import org.gradle.api.logging.Logger;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Copy;
import org.gradle.api.tasks.InputFile;
import org.gradle.maven.MavenModule;
import org.gradle.maven.MavenPomArtifact;

/**
* A Gradle Task to set up an environment for running Embulk.
*/
public class InstallEmbulkRunSet extends Copy {
public InstallEmbulkRunSet() {
super();

this.project = this.getProject();
this.logger = this.project.getLogger();

final ObjectFactory objectFactory = this.project.getObjects();
}

/**
* Adds a Maven artifact to be installed.
*
* <p>It tries to simulate Gradle's dependency notations, but it is yet far from perfect.
*
* @see <a href="https://github.com/gradle/gradle/blob/v8.4.0/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyNotationParser.java#L49-L86">org.gradle.api.internal.notations.DependencyNotationParser#create</a>
*/
public void artifact(final Object dependencyNotation) {
final Dependency dependency;
if (dependencyNotation instanceof CharSequence) {
dependency = this.dependencyFromCharSequence((CharSequence) dependencyNotation);
} else if (dependencyNotation instanceof Map) {
dependency = this.dependencyFromMap((Map) dependencyNotation);
} else {
throw new IllegalDependencyNotation("Supplied module notation is invalid.");
}

// Constructing an independent (detached) Configuration so that its dependencies are not affected by other plugins.
final Configuration configuration = this.project.getConfigurations().detachedConfiguration(dependency);

final ResolvableDependencies resolvavbleDependencies = configuration.getIncoming();
final ArtifactCollection artifactCollection = resolvavbleDependencies.getArtifacts();

// Getting the JAR files and component IDs.
final ArrayList<ComponentIdentifier> componentIds = new ArrayList<>();
for (final ResolvedArtifactResult resolvedArtifactResult : artifactCollection.getArtifacts()) {
componentIds.add(resolvedArtifactResult.getId().getComponentIdentifier());
this.fromArtifact(resolvedArtifactResult, "jar");
}

// Getting the POM files.
final ArtifactResolutionResult artifactResolutionResult = this.project.getDependencies()
.createArtifactResolutionQuery()
.forComponents(componentIds)
.withArtifacts(MavenModule.class, MavenPomArtifact.class)
.execute();
for (final ComponentArtifactsResult componentArtifactResult : artifactResolutionResult.getResolvedComponents()) {
for (final ArtifactResult artifactResult : componentArtifactResult.getArtifacts(MavenPomArtifact.class)) {
if (artifactResult instanceof ResolvedArtifactResult) {
final ResolvedArtifactResult resolvedArtifactResult = (ResolvedArtifactResult) artifactResult;
this.fromArtifact(resolvedArtifactResult, "pom");
}
}
}
}

private void fromArtifact(final ResolvedArtifactResult resolvedArtifactResult, final String artifactType) {
final ComponentIdentifier id = resolvedArtifactResult.getId().getComponentIdentifier();
final File file = resolvedArtifactResult.getFile();

if (id instanceof ModuleComponentIdentifier) {
final Path modulePath = moduleToPath((ModuleComponentIdentifier) id);
this.logger.info("Setting to copy {}:{} into {}", id, artifactType, modulePath);
this.logger.debug("Cached file: {}", file);
this.from(file, copy -> {
copy.into(modulePath.toFile());
copy.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE);
});
} else if (id instanceof ProjectComponentIdentifier) {
throw new IllegalDependencyNotation("Cannot install artifacts for a project component (" + id.getDisplayName() + ")");
} else {
throw new IllegalDependencyNotation(
"Cannot resolve the artifacts for component "
+ id.getDisplayName()
+ " with unsupported type "
+ id.getClass().getName()
+ ".");
}
}

private static Path moduleToPath(final ModuleComponentIdentifier id) {
final String[] splitGroup = id.getGroup().split("\\.");
if (splitGroup.length <= 0) {
return Paths.get("");
}

final String[] more = new String[splitGroup.length + 2 - 1];
for (int i = 1; i < splitGroup.length; i++) {
more[i - 1] = splitGroup[i];
}
more[splitGroup.length - 1] = id.getModule();
more[splitGroup.length] = id.getVersion();
final Path path = Paths.get(splitGroup[0], more);
assert !path.isAbsolute();
return path;
}

// https://github.com/gradle/gradle/blob/v8.4.0/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyStringNotationConverter.java
private Dependency dependencyFromCharSequence(final CharSequence dependencyNotation) {
final String notationString = dependencyNotation.toString();
this.logger.debug("Artifact: {}", notationString);
return this.project.getDependencies().create(notationString);
}

// https://github.com/gradle/gradle/blob/v8.4.0/subprojects/core/src/main/java/org/gradle/internal/typeconversion/MapNotationConverter.java
private Dependency dependencyFromMap(final Map dependencyNotation) {
final Map<String, String> notationMap = validateMap(dependencyNotation);
this.logger.debug("Artifact: {}", notationMap);
return this.project.getDependencies().create(notationMap);
}

private static Map<String, String> validateMap(final Map dependencyNotation) {
final LinkedHashMap<String, String> map = new LinkedHashMap<>();
for (final Map.Entry<Object, Object> entry : castMap(dependencyNotation).entrySet()) {
final Object keyObject = entry.getKey();
if (!(keyObject instanceof CharSequence)) {
throw new IllegalDependencyNotation("Supplied Map module notation is invalid. Its key must be a String.");
}
final String key = (String) keyObject;
if (!ACCEPTABLE_MAP_KEYS.contains(key)) {
throw new IllegalDependencyNotation(
"Supplied Map module notation is invalid. Its key must be one of: ["
+ String.join(", ", ACCEPTABLE_MAP_KEYS)
+ "]");
}

final Object valueObject = entry.getValue();
if (!(valueObject instanceof CharSequence)) {
throw new IllegalDependencyNotation("Supplied Map module notation is invalid. Its value must be a String.");
}
final String value = (String) valueObject;
map.put(key, value);
}
return Collections.unmodifiableMap(map);
}

@SuppressWarnings("unchecked")
private static Map<Object, Object> castMap(final Map map) {
return (Map<Object, Object>) map;
}

// https://github.com/gradle/gradle/blob/v8.4.0/subprojects/dependency-management/src/main/java/org/gradle/api/internal/notations/DependencyMapNotationConverter.java#L42-L58
private static List<String> ACCEPTABLE_MAP_KEYS =
Arrays.asList("group", "name", "version", "configuration", "ext", "classifier");

private final Logger logger;

private final Project project;
}
46 changes: 46 additions & 0 deletions src/test/java/org/embulk/gradle/runset/TestEmbulkRunSetPlugin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2022 The Embulk project
*
* 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
*
* http://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.embulk.gradle.runset;

import static org.embulk.gradle.runset.Util.prepareProjectDir;
import static org.embulk.gradle.runset.Util.runGradle;

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

public class TestEmbulkRunSetPlugin {
@Test
public void testSimple(@TempDir Path tempDir) throws IOException {
final Path projectDir = prepareProjectDir(tempDir, "simple");

runGradle(projectDir, "installEmbulkRunSet");

Files.walkFileTree(projectDir.resolve("build/simple"), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
System.out.println(projectDir.relativize(file));
return FileVisitResult.CONTINUE;
}
});
}
}
31 changes: 0 additions & 31 deletions src/test/java/org/embulk/gradle/runset/TestRunsetPlugin.java

This file was deleted.

Loading

0 comments on commit de00c83

Please sign in to comment.