Skip to content

Commit

Permalink
Error with duplicate classes on truetime android library (#222)
Browse files Browse the repository at this point in the history
* Using gradle shadow to merge embedded dependencies

* Creating shadowJar extension

* Setting truetime shadow relocation

* Updated changelog
  • Loading branch information
LikeTheSalad authored Oct 31, 2023
1 parent 5199095 commit 3f825ce
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 93 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ ${next_release_notes}
==== ${version} - ${release_date}

[float]
===== Features
===== Bug fixes

* New feature: {pull}000[#000]
* Fix truetime duplicated classes: {pull}222[#222]
////
[[release-notes-0.10.0]]
Expand Down
4 changes: 4 additions & 0 deletions android-sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ licensesConfig {
manualMappingFile = rootProject.file("manual_licenses_map.txt")
}

shadowJar {
relocate 'com.instacart.library.truetime', 'co.elastic.apm.android.truetime'
}

dependencies {
api "io.opentelemetry:opentelemetry-api:$openTelemetry_version"
api "io.opentelemetry:opentelemetry-sdk:$openTelemetry_version"
Expand Down
1 change: 1 addition & 0 deletions build-tools/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ dependencies {
implementation "org.jetbrains.dokka:dokka-gradle-plugin:1.9.0"
implementation "io.github.gradle-nexus:publish-plugin:1.3.0"
implementation 'com.gradle.publish:plugin-publish-plugin:1.2.1'
implementation "gradle.plugin.com.github.johnrengelman:shadow:7.1.2"
testImplementation "junit:junit:4.13.2"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,52 @@

import static co.elastic.apm.compile.tools.utils.Constants.ARTIFACT_TYPE_ATTR;

import com.android.build.api.artifact.MultipleArtifact;
import com.android.build.api.artifact.ScopedArtifact;
import com.android.build.api.variant.AndroidComponentsExtension;
import com.android.build.api.variant.ScopedArtifacts;
import com.android.build.api.variant.Variant;

import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.Directory;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.FileTree;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Sync;
import org.gradle.api.tasks.TaskProvider;

import java.io.File;
import java.util.concurrent.Callable;
import java.util.Collections;

import co.elastic.apm.compile.tools.embedding.tasks.EmbeddedClassesGathererTask;
import co.elastic.apm.compile.tools.embedding.extensions.ShadowExtension;
import co.elastic.apm.compile.tools.embedding.tasks.EmbeddedClassesMergerTask;
import kotlin.Unit;

@SuppressWarnings("unchecked")
public class EmbeddingDependenciesPlugin implements Plugin<Project> {

public static final String EMBEDDED_CLASSPATH_NAME = "embeddedClasspath";
private static final String SHADOW_EXTENSION_NAME = "shadowJar";
private ShadowExtension shadowExtension;

@Override
public void apply(Project project) {
AndroidComponentsExtension<?, ?, Variant> componentsExtension = project.getExtensions().getByType(AndroidComponentsExtension.class);
Configuration embeddedClasspath = getEmbeddedClasspath(project);
Provider<FileCollection> classesProvider = getClassesProvider(project, embeddedClasspath);
String embeddedClassesTaskName = "embeddedClasses";
Provider<Directory> classesDir = project.getLayout().getBuildDirectory().dir(embeddedClassesTaskName);
TaskProvider<Sync> syncEmbeddedClassesTask = project.getTasks().register(embeddedClassesTaskName, Sync.class, sync -> {
sync.from(classesProvider);
sync.into(classesDir);
});
shadowExtension = project.getExtensions().create(SHADOW_EXTENSION_NAME, ShadowExtension.class);

componentsExtension.onVariants(componentsExtension.selector().all(), variant -> {
TaskProvider<EmbeddedClassesGathererTask> taskProvider = getEmbeddedClassesGathererTaskProvider(project, classesDir, variant);
taskProvider.configure(task -> task.dependsOn(syncEmbeddedClassesTask));

variant.getArtifacts().use(taskProvider)
.wiredWith(EmbeddedClassesGathererTask::getOutputDir)
.toAppendTo(MultipleArtifact.ALL_CLASSES_DIRS.INSTANCE);
variant.getArtifacts().forScope(ScopedArtifacts.Scope.PROJECT).use(getEmbeddedClassesMergerTaskProvider(project, embeddedClasspath, variant))
.toTransform(ScopedArtifact.CLASSES.INSTANCE, EmbeddedClassesMergerTask::getInputJars,
EmbeddedClassesMergerTask::getLocalClassesDirs, EmbeddedClassesMergerTask::getOutputFile);
return Unit.INSTANCE;
});
}

private TaskProvider<EmbeddedClassesGathererTask> getEmbeddedClassesGathererTaskProvider(Project project, Provider<Directory> classesDir, Variant variant) {
TaskProvider<EmbeddedClassesGathererTask> taskProvider = project.getTasks().register(variant.getName() + "EmbeddedClassesGatherer", EmbeddedClassesGathererTask.class);
private TaskProvider<EmbeddedClassesMergerTask> getEmbeddedClassesMergerTaskProvider(Project project, Configuration embedded, Variant variant) {
TaskProvider<EmbeddedClassesMergerTask> taskProvider = project.getTasks().register(variant.getName() + "EmbeddedClassesMerger", EmbeddedClassesMergerTask.class);
taskProvider.configure(task -> {
task.getClassesDir().set(classesDir);
task.getOutputDir().set(project.getLayout().getBuildDirectory().dir(task.getName()));
task.from(task.getLocalClassesDirs());
task.setConfigurations(Collections.singletonList(embedded));
for (ShadowExtension.Relocation relocation : shadowExtension.getRelocations()) {
task.relocate(relocation.getPattern().get(), relocation.getDestination().get());
}
});
return taskProvider;
}
Expand All @@ -76,36 +68,4 @@ private Configuration getEmbeddedClasspath(Project project) {

return classpath;
}

private Provider<FileCollection> getClassesProvider(Project project, Configuration classpath) {
return project.provider(new LazyFileCollectionProvider(project, classpath));
}

private static class LazyFileCollectionProvider implements Callable<FileCollection> {
private final Project project;
private final Configuration classpath;
private FileCollection cachedFileCollection;

private LazyFileCollectionProvider(Project project, Configuration classpath) {
this.project = project;
this.classpath = classpath;
}

@Override
public FileCollection call() {
if (cachedFileCollection != null) {
return cachedFileCollection;
}
ConfigurableFileCollection fileCollection = project.files();
for (File file : classpath.getFiles()) {
if (file.getName().endsWith(".jar")) {
FileTree files = project.zipTree(file);
fileCollection.from(files);
}
}

cachedFileCollection = fileCollection;
return fileCollection;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package co.elastic.apm.compile.tools.embedding.extensions;

import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.Property;

import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;

public class ShadowExtension {
private final ObjectFactory objectFactory;

private final List<Relocation> relocations = new ArrayList<>();

@Inject
public ShadowExtension(ObjectFactory objectFactory) {
this.objectFactory = objectFactory;
}

public List<Relocation> getRelocations() {
return relocations;
}

public void relocate(String pattern, String destination) {
Relocation relocation = objectFactory.newInstance(Relocation.class);
relocation.getPattern().set(pattern);
relocation.getDestination().set(destination);
relocations.add(relocation);
}

public interface Relocation {
Property<String> getPattern();

Property<String> getDestination();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package co.elastic.apm.compile.tools.embedding.tasks;

import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar;

import org.gradle.api.file.Directory;
import org.gradle.api.file.RegularFile;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Internal;

public abstract class EmbeddedClassesMergerTask extends ShadowJar {

@InputFiles
public abstract ListProperty<RegularFile> getInputJars();

@InputFiles
public abstract ListProperty<Directory> getLocalClassesDirs();

@Internal
public RegularFileProperty getOutputFile() {
return (RegularFileProperty) getArchiveFile();
}
}

0 comments on commit 3f825ce

Please sign in to comment.