diff --git a/.editorconfig b/.editorconfig index 97c1b8453..f36ddfdde 100755 --- a/.editorconfig +++ b/.editorconfig @@ -7,9 +7,6 @@ indent_style = space insert_final_newline = true trim_trailing_whitespace = true -[*.{groovy,java}] -indent_size = 4 - [*.{kt,kts}] ij_kotlin_imports_layout = * ij_kotlin_allow_trailing_comma = true diff --git a/api/shadow.api b/api/shadow.api new file mode 100644 index 000000000..0a84b2a66 --- /dev/null +++ b/api/shadow.api @@ -0,0 +1,550 @@ +public abstract interface class com/github/jengelman/gradle/plugins/shadow/DependencyFilter { + public abstract fun dependency (Lgroovy/lang/Closure;)Lorg/gradle/api/specs/Spec; + public abstract fun dependency (Ljava/lang/Object;)Lorg/gradle/api/specs/Spec; + public abstract fun dependency (Lorg/gradle/api/artifacts/Dependency;)Lorg/gradle/api/specs/Spec; + public abstract fun exclude (Lorg/gradle/api/specs/Spec;)Lcom/github/jengelman/gradle/plugins/shadow/DependencyFilter; + public abstract fun include (Lorg/gradle/api/specs/Spec;)Lcom/github/jengelman/gradle/plugins/shadow/DependencyFilter; + public abstract fun project (Ljava/lang/String;)Lorg/gradle/api/specs/Spec; + public abstract fun project (Ljava/util/Map;)Lorg/gradle/api/specs/Spec; + public abstract fun resolve (Ljava/util/Collection;)Lorg/gradle/api/file/FileCollection; + public abstract fun resolve (Lorg/gradle/api/file/FileCollection;)Lorg/gradle/api/file/FileCollection; +} + +public final class com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin : org/gradle/api/Plugin { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin$Companion; + public static final field SHADOW_INSTALL_TASK_NAME Ljava/lang/String; + public static final field SHADOW_RUN_TASK_NAME Ljava/lang/String; + public static final field SHADOW_SCRIPTS_TASK_NAME Ljava/lang/String; + public fun ()V + public synthetic fun apply (Ljava/lang/Object;)V + public fun apply (Lorg/gradle/api/Project;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin$Companion { +} + +public final class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin : org/gradle/api/Plugin { + public static final field COMPONENT_NAME Ljava/lang/String; + public static final field CONFIGURATION_NAME Ljava/lang/String; + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin$Companion; + public static final field EXTENSION_NAME Ljava/lang/String; + public fun ()V + public synthetic fun apply (Ljava/lang/Object;)V + public fun apply (Lorg/gradle/api/Project;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin$Companion { +} + +public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowExtension { + public fun (Lorg/gradle/api/Project;)V + public final fun component (Lorg/gradle/api/publish/maven/MavenPublication;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin : org/gradle/api/Plugin { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin$Companion; + public static final field SHADOW_GROUP Ljava/lang/String; + public static final field SHADOW_JAR_TASK_NAME Ljava/lang/String; + public static final field SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME Ljava/lang/String; + public fun (Lorg/gradle/api/component/SoftwareComponentFactory;)V + public synthetic fun apply (Ljava/lang/Object;)V + public fun apply (Lorg/gradle/api/Project;)V + public static final fun configureShadowTask (Lorg/gradle/api/Project;Lorg/gradle/api/artifacts/Configuration;)Lorg/gradle/api/tasks/TaskProvider; +} + +public final class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin$Companion { + public final fun configureShadowTask (Lorg/gradle/api/Project;Lorg/gradle/api/artifacts/Configuration;)Lorg/gradle/api/tasks/TaskProvider; +} + +public final class com/github/jengelman/gradle/plugins/shadow/ShadowPlugin : org/gradle/api/Plugin { + public fun ()V + public synthetic fun apply (Ljava/lang/Object;)V + public fun apply (Lorg/gradle/api/Project;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/ShadowStats { + public fun ()V + public final fun finishJar ()V + public final fun getAverageTimePerJar ()D + public final fun getAverageTimeSecsPerJar ()D + public final fun getJarCount ()I + public final fun getJarEndTime ()J + public final fun getJarStartTime ()J + public final fun getJarTiming ()J + public final fun getProcessingJar ()Z + public final fun getRelocationString ()Ljava/lang/String; + public final fun getTotalTime ()J + public final fun getTotalTimeSecs ()D + public final fun printStats ()V + public final fun relocate (Ljava/lang/String;Ljava/lang/String;)V + public final fun setJarCount (I)V + public final fun setJarEndTime (J)V + public final fun setJarStartTime (J)V + public final fun setProcessingJar (Z)V + public final fun setTotalTime (J)V + public final fun startJar ()V + public fun toString ()Ljava/lang/String; +} + +public abstract interface class com/github/jengelman/gradle/plugins/shadow/ZipCompressor : org/gradle/api/internal/file/archive/compression/ArchiveOutputStreamFactory { +} + +public final class com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin : org/gradle/api/Plugin { + public fun ()V + public synthetic fun apply (Ljava/lang/Object;)V + public fun apply (Lorg/gradle/api/Project;)V +} + +public abstract interface annotation class com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator : java/lang/annotation/Annotation { +} + +public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext { + public fun (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; + public final fun copy (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext; + public static synthetic fun copy$default (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILjava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext; + public fun equals (Ljava/lang/Object;)Z + public final fun getClassName ()Ljava/lang/String; + public final fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext { + public fun (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; + public final fun copy (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext; + public static synthetic fun copy$default (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILjava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext; + public fun equals (Ljava/lang/Object;)Z + public final fun getPath ()Ljava/lang/String; + public final fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract interface class com/github/jengelman/gradle/plugins/shadow/relocation/Relocator { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator$Companion; + public static final field ROLE Ljava/lang/String; + public abstract fun applyToSourceContent (Ljava/lang/String;)Ljava/lang/String; + public abstract fun canRelocateClass (Ljava/lang/String;)Z + public abstract fun canRelocatePath (Ljava/lang/String;)Z + public abstract fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;)Ljava/lang/String; + public abstract fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;)Ljava/lang/String; +} + +public final class com/github/jengelman/gradle/plugins/shadow/relocation/Relocator$Companion { +} + +public final class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator : com/github/jengelman/gradle/plugins/shadow/relocation/Relocator { + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Z)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun applyToSourceContent (Ljava/lang/String;)Ljava/lang/String; + public fun canRelocateClass (Ljava/lang/String;)Z + public fun canRelocatePath (Ljava/lang/String;)Z + public final fun exclude (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; + public final fun getExcludes ()Ljava/util/Set; + public final fun getIncludes ()Ljava/util/Set; + public final fun getPathPattern ()Ljava/lang/String; + public final fun getPattern ()Ljava/lang/String; + public final fun getShadedPathPattern ()Ljava/lang/String; + public final fun getShadedPattern ()Ljava/lang/String; + public final fun include (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; + public fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;)Ljava/lang/String; + public fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;)Ljava/lang/String; +} + +public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest : org/gradle/api/java/archives/Manifest { + public abstract fun inheritFrom ([Ljava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; + public abstract fun inheritFrom ([Ljava/lang/Object;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; +} + +public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask : org/gradle/api/DefaultTask { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask$Companion; + public static final field DESC Ljava/lang/String; + public static final field NAME Ljava/lang/String; + public fun ()V + public final fun knows ()V +} + +public final class com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask$Companion { +} + +public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction : org/gradle/api/internal/file/copy/CopyAction { + public static final field CONSTANT_TIME_FOR_ZIP_ENTRIES J + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$Companion; + public fun execute (Lorg/gradle/api/internal/file/copy/CopyActionProcessingStream;)Lorg/gradle/api/tasks/WorkResult; +} + +public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$ArchiveFileTreeElement : org/gradle/api/file/FileTreeElement { + public fun (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath;)V + public final fun asFileTreeElement ()Lorg/gradle/api/file/FileTreeElement; + public fun copyTo (Ljava/io/File;)Z + public fun copyTo (Ljava/io/OutputStream;)V + public fun getFile ()Ljava/io/File; + public fun getLastModified ()J + public fun getMode ()I + public fun getName ()Ljava/lang/String; + public fun getPath ()Ljava/lang/String; + public fun getPermissions ()Lorg/gradle/api/file/FilePermissions; + public fun getRelativePath ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath; + public synthetic fun getRelativePath ()Lorg/gradle/api/file/RelativePath; + public fun getSize ()J + public final fun isClassFile ()Z + public fun isDirectory ()Z + public fun open ()Ljava/io/InputStream; +} + +public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$BaseStreamAction : org/gradle/api/internal/file/CopyActionProcessingStreamAction { + public fun ()V + protected final fun isArchive (Lorg/gradle/api/file/FileCopyDetails;)Z + protected final fun isClass (Lorg/gradle/api/file/FileCopyDetails;)Z + public fun processFile (Lorg/gradle/api/internal/file/copy/FileCopyDetailsInternal;)V + protected fun visitDir (Lorg/gradle/api/file/FileCopyDetails;)V + protected abstract fun visitFile (Lorg/gradle/api/file/FileCopyDetails;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$Companion { +} + +public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath : org/gradle/api/file/RelativePath { + public fun (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction;Lorg/apache/tools/zip/ZipEntry;)V + public final fun charAt (I)C + public fun get (I)C + public final fun getEntry ()Lorg/apache/tools/zip/ZipEntry; + public fun getLength ()I + public fun getParent ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath; + public synthetic fun getParent ()Lorg/gradle/api/file/RelativePath; + public final fun isClassFile ()Z + public final fun length ()I +} + +public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar : org/gradle/api/tasks/bundling/Jar, com/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec { + public fun ()V + public synthetic fun append (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec; + public fun append (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + protected fun copy ()V + protected fun createCopyAction ()Lorg/gradle/api/internal/file/copy/CopyAction; + public synthetic fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec; + public fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public final fun getApiJars ()Lorg/gradle/api/file/FileCollection; + public final fun getConfigurations ()Ljava/util/List; + public final fun getDependencyFilter ()Lcom/github/jengelman/gradle/plugins/shadow/DependencyFilter; + public final fun getEnableRelocation ()Z + public final fun getIncludedDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; + public final fun getInternalCompressor ()Lcom/github/jengelman/gradle/plugins/shadow/ZipCompressor; + public fun getManifest ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; + public synthetic fun getManifest ()Lorg/gradle/api/java/archives/Manifest; + public final fun getRelocationPrefix ()Ljava/lang/String; + public final fun getRelocators ()Ljava/util/List; + public final fun getRootPatternSet ()Lorg/gradle/api/tasks/util/PatternSet; + public final fun getSourceSetsClassesDirs ()Lorg/gradle/api/file/FileCollection; + public fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; + public final fun getToMinimize ()Lorg/gradle/api/file/FileCollection; + public final fun getTransformers ()Ljava/util/List; + public synthetic fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec; + public fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun mergeServiceFiles ()Lcom/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec; + public fun mergeServiceFiles ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun mergeServiceFiles (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec; + public fun mergeServiceFiles (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun mergeServiceFiles (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec; + public fun mergeServiceFiles (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun minimize ()Lcom/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec; + public fun minimize ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun minimize (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec; + public fun minimize (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;)Lcom/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec; + public fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun relocate (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec; + public fun relocate (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun relocate (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec; + public fun relocate (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun relocate (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec; + public fun relocate (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec; + public fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public final fun setConfigurations (Ljava/util/List;)V + public final fun setDependencyFilter (Lcom/github/jengelman/gradle/plugins/shadow/DependencyFilter;)V + public final fun setEnableRelocation (Z)V + public final fun setRelocationPrefix (Ljava/lang/String;)V + public final fun setRelocators (Ljava/util/List;)V + public final fun setTransformers (Ljava/util/List;)V + public synthetic fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Lcom/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec; + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun transform (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec; + public fun transform (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun transform (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec; + public fun transform (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public static final field LICENSE_PATH Ljava/lang/String; + public static final field LICENSE_TXT_PATH Ljava/lang/String; + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun getName ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public static final field NOTICE_PATH Ljava/lang/String; + public static final field NOTICE_TXT_PATH Ljava/lang/String; + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public final fun getAddHeader ()Z + public final fun getCopyright ()Ljava/lang/String; + public final fun getEncoding ()Ljava/nio/charset/Charset; + public final fun getInceptionYear ()Ljava/lang/String; + public fun getName ()Ljava/lang/String; + public final fun getOrganizationName ()Ljava/lang/String; + public final fun getOrganizationURL ()Ljava/lang/String; + public final fun getPreamble1 ()Ljava/lang/String; + public final fun getPreamble2 ()Ljava/lang/String; + public final fun getPreamble3 ()Ljava/lang/String; + public final fun getProjectName ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public final fun setAddHeader (Z)V + public final fun setCopyright (Ljava/lang/String;)V + public final fun setEncoding (Ljava/nio/charset/Charset;)V + public final fun setInceptionYear (Ljava/lang/String;)V + public final fun setOrganizationName (Ljava/lang/String;)V + public final fun setOrganizationURL (Ljava/lang/String;)V + public final fun setPreamble1 (Ljava/lang/String;)V + public final fun setPreamble2 (Ljava/lang/String;)V + public final fun setPreamble3 (Ljava/lang/String;)V + public final fun setProjectName (Ljava/lang/String;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun getName ()Ljava/lang/String; + public final fun getResource ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public final fun setResource (Ljava/lang/String;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public abstract interface annotation class com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer : java/lang/annotation/Annotation { +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public static final field COMPONENTS_XML_PATH Ljava/lang/String; + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer$Companion; + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun getName ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer$Companion { +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun getName ()Ljava/lang/String; + public final fun getResource ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public final fun setResource (Ljava/lang/String;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public static final field EXTENSION_CLASSES_KEY Ljava/lang/String; + public static final field GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH Ljava/lang/String; + public static final field GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH Ljava/lang/String; + public static final field MERGED_MODULE_NAME Ljava/lang/String; + public static final field MERGED_MODULE_VERSION Ljava/lang/String; + public static final field MODULE_NAME_KEY Ljava/lang/String; + public static final field MODULE_VERSION_KEY Ljava/lang/String; + public static final field STATIC_EXTENSION_CLASSES_KEY Ljava/lang/String; + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun getName ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public field file Ljava/io/File; + public field resource Ljava/lang/String; + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public final fun getFile ()Ljava/io/File; + public fun getName ()Ljava/lang/String; + public final fun getResource ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public final fun setFile (Ljava/io/File;)V + public final fun setResource (Ljava/lang/String;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public static final field PLUGIN_CACHE_FILE Ljava/lang/String; + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun getName ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public fun ()V + public final fun append (Ljava/lang/String;Ljava/lang/Comparable;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer; + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public final fun getAttributes ()Ljava/util/List; + public fun getName ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public fun ()V + public final fun attributes (Ljava/util/Map;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer; + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public final fun getMainClass ()Ljava/lang/String; + public final fun getManifestEntries ()Ljava/util/Map; + public fun getName ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public final fun setMainClass (Ljava/lang/String;)V + public final fun setManifestEntries (Ljava/util/Map;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/NoOpTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public static final field INSTANCE Lcom/github/jengelman/gradle/plugins/shadow/transformers/NoOpTransformer; + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun getName ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public static final field PROPERTIES_SUFFIX Ljava/lang/String; + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public final fun getCharset ()Ljava/nio/charset/Charset; + public final fun getKeyTransformer ()Lkotlin/jvm/functions/Function1; + public final fun getMappings ()Ljava/util/Map; + public final fun getMergeSeparator ()Ljava/lang/String; + public final fun getMergeStrategy ()Ljava/lang/String; + public fun getName ()Ljava/lang/String; + public final fun getPaths ()Ljava/util/List; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public final fun setCharset (Ljava/nio/charset/Charset;)V + public final fun setKeyTransformer (Lkotlin/jvm/functions/Function1;)V + public final fun setMappings (Ljava/util/Map;)V + public final fun setMergeSeparator (Ljava/lang/String;)V + public final fun setMergeStrategy (Ljava/lang/String;)V + public final fun setPaths (Ljava/util/List;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer, org/gradle/api/tasks/util/PatternFilterable { + public static final field GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN Ljava/lang/String; + public static final field SERVICES_PATTERN Ljava/lang/String; + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun exclude (Lgroovy/lang/Closure;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun exclude (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun exclude (Lorg/gradle/api/specs/Spec;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun exclude ([Ljava/lang/String;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun getExcludes ()Ljava/util/Set; + public fun getIncludes ()Ljava/util/Set; + public fun getName ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun include (Lgroovy/lang/Closure;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun include (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun include (Lorg/gradle/api/specs/Spec;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun include ([Ljava/lang/String;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun setExcludes (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun setIncludes (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; + public final fun setPath (Ljava/lang/String;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer$ServiceStream : java/io/ByteArrayOutputStream { + public fun ()V + public final fun append (Ljava/io/InputStream;)V + public final fun toInputStream ()Ljava/io/InputStream; +} + +public abstract interface class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer : org/gradle/api/Named { + public abstract fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public abstract fun getName ()Ljava/lang/String; + public abstract fun hasTransformedResource ()Z + public abstract fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public abstract fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer$DefaultImpls { + public static fun getName (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Ljava/lang/String; +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Companion; + public fun (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/List;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V + public static final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/io/InputStream; + public final fun component3 ()Ljava/util/List; + public final fun component4 ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; + public final fun copy (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/List;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; + public static synthetic fun copy$default (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;Ljava/lang/String;Ljava/io/InputStream;Ljava/util/List;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILjava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; + public fun equals (Ljava/lang/Object;)Z + public static final fun getEntryTimestamp (ZJ)J + public final fun getInputStream ()Ljava/io/InputStream; + public final fun getPath ()Ljava/lang/String; + public final fun getRelocators ()Ljava/util/List; + public final fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder { + public fun ()V + public final fun build ()Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; + public final fun inputStream (Ljava/io/InputStream;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; + public final fun path (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; + public final fun relocators (Ljava/util/List;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; + public final fun stats (Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Companion { + public final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; + public final fun getEntryTimestamp (ZJ)J +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public final fun getIgnoreDtd ()Z + public fun getName ()Ljava/lang/String; + public final fun getResource ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public final fun setIgnoreDtd (Z)V + public final fun setResource (Ljava/lang/String;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + diff --git a/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts b/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts index fa9f96d82..fe518e5c5 100644 --- a/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts +++ b/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts @@ -7,11 +7,6 @@ version = providers.gradleProperty("VERSION_NAME").get() group = providers.gradleProperty("GROUP").get() description = providers.gradleProperty("POM_DESCRIPTION").get() -java { - withSourcesJar() - withJavadocJar() -} - gradlePlugin { website = providers.gradleProperty("POM_URL") vcsUrl = providers.gradleProperty("POM_URL") diff --git a/build.gradle.kts b/build.gradle.kts index 495a3a42e..de9fa1dfb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,9 +1,14 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinVersion + plugins { + kotlin("jvm") version "2.0.20" groovy `java-gradle-plugin` id("shadow.convention.publish") id("shadow.convention.deploy") id("com.diffplug.spotless") version "7.0.0.BETA2" + id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.16.3" } java { @@ -11,7 +16,19 @@ java { targetCompatibility = JavaVersion.VERSION_11 } +kotlin { + explicitApi() + compilerOptions { + // https://docs.gradle.org/current/userguide/compatibility.html#kotlin + apiVersion = KotlinVersion.KOTLIN_1_8 + jvmTarget = JvmTarget.JVM_11 + } +} + spotless { + kotlin { + ktlint() + } kotlinGradle { ktlint() target("**/*.kts") diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.groovy deleted file mode 100644 index 7a57952ae..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.groovy +++ /dev/null @@ -1,139 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import com.github.jengelman.gradle.plugins.shadow.internal.JavaJarExec -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import org.gradle.api.GradleException -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.distribution.Distribution -import org.gradle.api.distribution.DistributionContainer -import org.gradle.api.file.CopySpec -import org.gradle.api.plugins.ApplicationPlugin -import org.gradle.api.plugins.JavaApplication -import org.gradle.api.plugins.JavaPluginExtension -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.Sync -import org.gradle.api.tasks.TaskProvider -import org.gradle.api.tasks.application.CreateStartScripts -import org.gradle.jvm.toolchain.JavaLauncher -import org.gradle.jvm.toolchain.JavaToolchainService - -class ShadowApplicationPlugin implements Plugin { - - public static final String SHADOW_RUN_TASK_NAME = 'runShadow' - public static final String SHADOW_SCRIPTS_TASK_NAME = 'startShadowScripts' - public static final String SHADOW_INSTALL_TASK_NAME = 'installShadowDist' - - private Project project - private JavaApplication javaApplication - - @Override - void apply(Project project) { - this.project = project - this.javaApplication = project.extensions.getByType(JavaApplication) - - DistributionContainer distributions = project.extensions.getByName("distributions") as DistributionContainer - Distribution distribution = distributions.create("shadow") - - addRunTask(project) - addCreateScriptsTask(project) - - configureDistSpec(project, distribution.contents) - - configureJarMainClass(project) - configureInstallTask(project) - } - - protected void configureJarMainClass(Project project) { - def classNameProvider = javaApplication.mainClass - jar.configure { jar -> - jar.inputs.property('mainClassName', classNameProvider) - jar.doFirst { - jar.manifest.attributes 'Main-Class': classNameProvider.get() - } - } - } - - protected void addRunTask(Project project) { - - project.tasks.register(SHADOW_RUN_TASK_NAME, JavaJarExec) { run -> - def install = project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync) - run.dependsOn SHADOW_INSTALL_TASK_NAME - run.mainClass.set('-jar') - run.description = 'Runs this project as a JVM application using the shadow jar' - run.group = ApplicationPlugin.APPLICATION_GROUP - run.conventionMapping.jvmArgs = { javaApplication.applicationDefaultJvmArgs } - run.conventionMapping.jarFile = { - project.file("${install.get().destinationDir.path}/lib/${jar.get().archiveFile.get().asFile.name}") - } - configureJavaLauncher(run) - } - } - - private void configureJavaLauncher(JavaJarExec run) { - def toolchain = project.getExtensions().getByType(JavaPluginExtension.class).toolchain - JavaToolchainService service = project.getExtensions().getByType(JavaToolchainService.class) - Provider defaultLauncher = service.launcherFor(toolchain) - run.getJavaLauncher().set(defaultLauncher) - } - - protected void addCreateScriptsTask(Project project) { - project.tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts) { startScripts -> - startScripts.unixStartScriptGenerator.template = project.resources.text.fromString(this.class.getResource("internal/unixStartScript.txt").text) - startScripts.windowsStartScriptGenerator.template = project.resources.text.fromString(this.class.getResource("internal/windowsStartScript.txt").text) - startScripts.description = 'Creates OS specific scripts to run the project as a JVM application using the shadow jar' - startScripts.group = ApplicationPlugin.APPLICATION_GROUP - startScripts.classpath = project.files(jar) - startScripts.conventionMapping.mainClassName = { javaApplication.mainClass.get() } - startScripts.conventionMapping.applicationName = { javaApplication.applicationName } - startScripts.conventionMapping.outputDir = { new File(project.layout.buildDirectory.asFile.get(), 'scriptsShadow') } - startScripts.conventionMapping.defaultJvmOpts = { javaApplication.applicationDefaultJvmArgs } - startScripts.inputs.files project.objects.fileCollection().from { -> jar } - } - } - - protected void configureInstallTask(Project project) { - project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync).configure { task -> - task.doFirst { - if (task.destinationDir.directory) { - if (task.destinationDir.listFiles().size() != 0 && (!new File(task.destinationDir, 'lib').directory || !new File(task.destinationDir, 'bin').directory)) { - throw new GradleException("The specified installation directory '${task.destinationDir}' is neither empty nor does it contain an installation for '${javaApplication.applicationName}'.\n" + - "If you really want to install to this directory, delete it and run the install task again.\n" + - "Alternatively, choose a different installation directory." - ) - } - } - } - task.doLast { - task.eachFile { - if (it.path == "bin/${javaApplication.applicationName}") { - it.mode = 0x755 - } - } - } - } - } - - protected CopySpec configureDistSpec(Project project, CopySpec distSpec) { - def startScripts = project.tasks.named(SHADOW_SCRIPTS_TASK_NAME) - - distSpec.with { - from(project.file("src/dist")) - - into("lib") { - from(jar) - from(project.configurations.shadow) - } - into("bin") { - from(startScripts) - filePermissions { it.unix(493) } - } - } - - distSpec - } - - private TaskProvider getJar() { - project.tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME, ShadowJar) - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.groovy deleted file mode 100644 index ccb64cf5c..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.groovy +++ /dev/null @@ -1,32 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import com.github.jengelman.gradle.plugins.shadow.tasks.KnowsTask -import org.gradle.api.GradleException -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.util.GradleVersion - -class ShadowBasePlugin implements Plugin { - - public static final String EXTENSION_NAME = 'shadow' - public static final String CONFIGURATION_NAME = 'shadow' - public static final String COMPONENT_NAME = 'shadow' - - @Override - void apply(Project project) { - if (GradleVersion.current() < GradleVersion.version("8.3")) { - throw new GradleException("This version of Shadow supports Gradle 8.3+ only. Please upgrade.") - } - project.extensions.create(EXTENSION_NAME, ShadowExtension, project) - createShadowConfiguration(project) - - project.tasks.register(KnowsTask.NAME, KnowsTask) { knows -> - knows.group = ShadowJavaPlugin.SHADOW_GROUP - knows.description = KnowsTask.DESC - } - } - - private static void createShadowConfiguration(Project project) { - project.configurations.create(CONFIGURATION_NAME) - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.groovy deleted file mode 100644 index 53fc8b38d..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.groovy +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import org.gradle.api.Project -import org.gradle.api.component.SoftwareComponentContainer -import org.gradle.api.publish.maven.MavenPublication - -@Deprecated -class ShadowExtension { - private final SoftwareComponentContainer components - - ShadowExtension(Project project) { - components = project.components - } - - /** - * @deprecated configure publication using component.shadow directly. - */ - @Deprecated - void component(MavenPublication publication) { - publication.from(components.findByName("shadow")) - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.groovy deleted file mode 100644 index 7a904a218..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.groovy +++ /dev/null @@ -1,108 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.attributes.Bundling -import org.gradle.api.attributes.Category -import org.gradle.api.attributes.LibraryElements -import org.gradle.api.attributes.Usage -import org.gradle.api.component.AdhocComponentWithVariants -import org.gradle.api.component.SoftwareComponentFactory -import org.gradle.api.plugins.JavaPlugin -import org.gradle.api.tasks.SourceSetContainer -import org.gradle.api.tasks.TaskProvider -import org.gradle.jvm.tasks.Jar -import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin - -import javax.inject.Inject - -class ShadowJavaPlugin implements Plugin { - - public static final String SHADOW_JAR_TASK_NAME = 'shadowJar' - public static final String SHADOW_GROUP = 'Shadow' - public static final String SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME = 'shadowRuntimeElements' - - private final SoftwareComponentFactory softwareComponentFactory - - @Inject - ShadowJavaPlugin(SoftwareComponentFactory softwareComponentFactory) { - this.softwareComponentFactory = softwareComponentFactory - } - - @Override - void apply(Project project) { - def shadowConfiguration = project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) - def shadowTaskProvider = configureShadowTask(project, shadowConfiguration) - - project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) { - it.extendsFrom(shadowConfiguration) - } - - def shadowRuntimeElements = project.configurations.create(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) { Configuration it -> - it.extendsFrom(shadowConfiguration) - it.canBeConsumed = true - it.canBeResolved = false - it.attributes { - it.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, Usage.JAVA_RUNTIME)) - it.attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category, Category.LIBRARY)) - it.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements, LibraryElements.JAR)) - it.attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling, Bundling.SHADOWED)) - } - it.outgoing.artifact(shadowTaskProvider) - } - - project.components.named("java", AdhocComponentWithVariants) { - it.addVariantsFromConfiguration(shadowRuntimeElements) { - it.mapToOptional() - } - } - - AdhocComponentWithVariants shadowComponent = softwareComponentFactory.adhoc(ShadowBasePlugin.COMPONENT_NAME) - project.components.add(shadowComponent) - shadowComponent.addVariantsFromConfiguration(shadowRuntimeElements) { - it.mapToMavenScope("runtime") - } - - project.plugins.withType(JavaGradlePluginPlugin).configureEach { - // Remove the gradleApi so it isn't merged into the jar file. - // This is required because 'java-gradle-plugin' adds gradleApi() to the 'api' configuration. - // See https://github.com/gradle/gradle/blob/972c3e5c6ef990dd2190769c1ce31998a9402a79/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java#L161 - project.configurations.named(JavaPlugin.API_CONFIGURATION_NAME) { - it.dependencies.remove(project.dependencies.gradleApi()) - } - // Compile only gradleApi() to make sure the plugin can compile against Gradle API. - project.configurations.named(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME) { - it.dependencies.add(project.dependencies.gradleApi()) - } - } - } - - protected static TaskProvider configureShadowTask(Project project, Configuration shadowConfiguration) { - SourceSetContainer sourceSets = project.extensions.getByType(SourceSetContainer) - def jarTask = project.tasks.named(JavaPlugin.JAR_TASK_NAME, Jar) - def taskProvider = project.tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar) { shadow -> - shadow.group = SHADOW_GROUP - shadow.description = 'Create a combined JAR of project and runtime dependencies' - shadow.archiveClassifier.set("all") - shadow.manifest.inheritFrom(jarTask.get().manifest) - def attrProvider = jarTask.map { it.manifest.attributes.get('Class-Path') } - def files = project.objects.fileCollection().from(shadowConfiguration) - shadow.doFirst { - if (!files.empty) { - def attrs = [attrProvider.getOrElse('')] + files.collect { it.name } - shadow.manifest.attributes 'Class-Path': attrs.join(' ').trim() - } - } - shadow.from(sourceSets.main.output) - shadow.configurations = [ - project.configurations.findByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) ?: - project.configurations.runtime, - ] - shadow.exclude('META-INF/INDEX.LIST', 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'module-info.class') - } - project.artifacts.add(shadowConfiguration.name, taskProvider) - return taskProvider - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.groovy deleted file mode 100644 index 3e9bbbc37..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.groovy +++ /dev/null @@ -1,52 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.plugins.ApplicationPlugin -import org.gradle.api.plugins.JavaPlugin - -class ShadowPlugin implements Plugin { - - @Override - void apply(Project project) { - project.with { - plugins.apply(ShadowBasePlugin) - plugins.apply(LegacyShadowPlugin) - plugins.withType(JavaPlugin) { - plugins.apply(ShadowJavaPlugin) - } - plugins.withType(ApplicationPlugin) { - plugins.apply(ShadowApplicationPlugin) - } - - boolean enableDevelocityIntegration = providers.gradleProperty( - "com.gradleup.shadow.enableDevelocityIntegration" - ).map { it.toBoolean() }.getOrElse(false) - if (enableDevelocityIntegration) { - // Legacy build scan support for Gradle Enterprise, users should migrate to develocity plugin. - rootProject.plugins.withId('com.gradle.enterprise') { - configureBuildScan(rootProject) - } - rootProject.plugins.withId('com.gradle.develocity') { - configureBuildScan(rootProject) - } - } - } - } - - private void configureBuildScan(Project rootProject) { - rootProject.buildScan.buildFinished { - def shadowTasks = tasks.withType(ShadowJar) - shadowTasks.each { task -> - if (task.didWork) { - task.stats.buildScanData.each { k, v -> - rootProject.buildScan.value "shadow.${task.path}.${k}", v.toString() - } - rootProject.buildScan.value "shadow.${task.path}.configurations", task.configurations*.name.join(", ") - } - } - } - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowStats.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowStats.groovy deleted file mode 100644 index 8813dc61b..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowStats.groovy +++ /dev/null @@ -1,81 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import groovy.util.logging.Slf4j -import org.gradle.api.GradleException - -@Slf4j -class ShadowStats { - - long totalTime - long jarStartTime - long jarEndTime - int jarCount = 1 - boolean processingJar - Map relocations = [:] - - void relocate(String src, String dst) { - relocations[src] = dst - } - - String getRelocationString() { - def maxLength = relocations.keySet().collect { it.length() }.max() - relocations.collect { k, v -> "${k} ${separator(k, maxLength)} ${v}"}.sort().join("\n") - } - - static String separator(String key, int max) { - return "→" - } - - void startJar() { - if (processingJar) throw new GradleException("Can only time one entry at a time") - processingJar = true - jarStartTime = System.currentTimeMillis() - } - - void finishJar() { - if (processingJar) { - jarEndTime = System.currentTimeMillis() - jarCount++ - totalTime += jarTiming - processingJar = false - } - } - - void printStats() { - println this - } - - long getJarTiming() { - jarEndTime - jarStartTime - } - - double getTotalTimeSecs() { - totalTime / 1000 - } - - double getAverageTimePerJar() { - totalTime / jarCount - } - - double getAverageTimeSecsPerJar() { - averageTimePerJar / 1000 - } - - String toString() { - StringBuilder sb = new StringBuilder() - sb.append "*******************\n" - sb.append "GRADLE SHADOW STATS\n" - sb.append "\n" - sb.append "Total Jars: $jarCount (includes project)\n" - sb.append "Total Time: ${totalTimeSecs}s [${totalTime}ms]\n" - sb.append "Average Time/Jar: ${averageTimeSecsPerJar}s [${averageTimePerJar}ms]\n" - sb.append "*******************" - } - - Map getBuildScanData() { - [ - dependencies: jarCount, - relocations : relocationString, - ] as Map - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.groovy deleted file mode 100644 index 5e23269e4..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.groovy +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.impl - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocatePathContext -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.RelativeArchivePath -import org.objectweb.asm.commons.Remapper - -import java.util.regex.Matcher -import java.util.regex.Pattern - -/** - * Modified from org.apache.maven.plugins.shade.DefaultShader.java#RelocatorRemapper - * - * @author John Engelman - */ -class RelocatorRemapper extends Remapper { - - private final Pattern classPattern = Pattern.compile("(\\[*)?L(.+)") - - List relocators - ShadowStats stats - - RelocatorRemapper(List relocators, ShadowStats stats) { - this.relocators = relocators - this.stats = stats - } - - boolean hasRelocators() { - return !relocators.empty - } - - @Override - Object mapValue(Object object) { - if (object instanceof String) { - String name = (String) object - String value = name - - String prefix = "" - String suffix = "" - - Matcher m = classPattern.matcher(name) - if (m.matches()) { - prefix = m.group(1) + "L" - suffix = "" - name = m.group(2) - } - - for (Relocator r : relocators) { - if (r.canRelocateClass(name)) { - RelocateClassContext classContext = RelocateClassContext.builder().className(name).stats(stats).build() - value = prefix + r.relocateClass(classContext) + suffix - break - } else if (r.canRelocatePath(name)) { - RelocatePathContext pathContext = RelocatePathContext.builder().path(name).stats(stats).build() - value = prefix + r.relocatePath(pathContext) + suffix - break - } - } - - return value - } - - return super.mapValue(object) - } - - @Override - String map(String name) { - String value = name - - String prefix = "" - String suffix = "" - - Matcher m = classPattern.matcher(name) - if (m.matches()) { - prefix = m.group(1) + "L" - suffix = "" - name = m.group(2) - } - - for (Relocator r : relocators) { - if (r.canRelocatePath(name)) { - RelocatePathContext pathContext = RelocatePathContext.builder().path(name).stats(stats).build() - value = prefix + r.relocatePath(pathContext) + suffix - break - } - } - - return value - } - - String mapPath(String path) { - map(path.substring(0, path.indexOf('.'))) - } - - String mapPath(RelativeArchivePath path) { - mapPath(path.pathString) - } - -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.groovy deleted file mode 100644 index 3232a9af9..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.groovy +++ /dev/null @@ -1,128 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import groovy.util.logging.Slf4j -import org.gradle.api.Project -import org.gradle.api.artifacts.Dependency -import org.gradle.api.artifacts.ResolvedDependency -import org.gradle.api.file.FileCollection -import org.gradle.api.specs.Spec -import org.gradle.api.specs.Specs - -@Slf4j -abstract class AbstractDependencyFilter implements DependencyFilter { - private final Project project - - protected final List> includeSpecs = [] - protected final List> excludeSpecs = [] - - AbstractDependencyFilter(Project project) { - assert project - this.project = project - } - - abstract protected void resolve(Set dependencies, - Set includedDependencies, - Set excludedDependencies) - - @Override - FileCollection resolve(FileCollection configuration) { - Set includedDeps = [] - Set excludedDeps = [] - resolve(configuration.resolvedConfiguration.firstLevelModuleDependencies, includedDeps, excludedDeps) - return project.files(configuration.files) - project.files(excludedDeps.collect { - it.moduleArtifacts*.file - }.flatten()) - } - - @Override - FileCollection resolve(Collection configurations) { - configurations.collect { - resolve(it) - }.sum() as FileCollection ?: project.files() - } - - /** - * Exclude dependencies that match the provided spec. - * - * @param spec - * @return - */ - @Override - DependencyFilter exclude(Spec spec) { - excludeSpecs << spec - return this - } - - /** - * Include dependencies that match the provided spec. - * - * @param spec - * @return - */ - @Override - DependencyFilter include(Spec spec) { - includeSpecs << spec - return this - } - - /** - * Create a spec that matches the provided project notation on group, name, and version - * @param notation - * @return - */ - @Override - Spec project(Map notation) { - dependency(project.dependencies.project(notation)) - } - - /** - * Create a spec that matches the default configuration for the provided project path on group, name, and version - * - * @param notation - * @return - */ - @Override - Spec project(String notation) { - dependency(project.dependencies.project(path: notation, configuration: 'default')) - } - - /** - * Create a spec that matches dependencies using the provided notation on group, name, and version - * @param notation - * @return - */ - @Override - Spec dependency(Object notation) { - dependency(project.dependencies.create(notation)) - } - - /** - * Create a spec that matches the provided dependency on group, name, and version - * @param dependency - * @return - */ - @Override - Spec dependency(Dependency dependency) { - this.dependency({ ResolvedDependency it -> - (!dependency.group || it.moduleGroup.matches(dependency.group)) && - (!dependency.name || it.moduleName.matches(dependency.name)) && - (!dependency.version || it.moduleVersion.matches(dependency.version)) - }) - } - - /** - * Create a spec that matches the provided closure - * @param spec - * @return - */ - @Override - Spec dependency(Closure spec) { - return Specs.convertClosureToSpec(spec) - } - - protected boolean isIncluded(ResolvedDependency dependency) { - boolean include = includeSpecs.empty || includeSpecs.any { it.isSatisfiedBy(dependency) } - boolean exclude = !excludeSpecs.empty && excludeSpecs.any { it.isSatisfiedBy(dependency) } - return include && !exclude - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.groovy deleted file mode 100644 index b54b5271b..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.groovy +++ /dev/null @@ -1,34 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -class CleanProperties extends Properties { - - private static class StripCommentsWithTimestampBufferedWriter extends BufferedWriter { - - private final int lengthOfExpectedTimestamp - - StripCommentsWithTimestampBufferedWriter(final Writer out) { - super(out) - - lengthOfExpectedTimestamp = ("#" + new Date().toString()).length() - } - - @Override - void write(final String str) throws IOException { - if (couldBeCommentWithTimestamp(str)) { - return - } - super.write(str) - } - - private boolean couldBeCommentWithTimestamp(final String str) { - return str != null && - str.startsWith("#") && - str.length() == lengthOfExpectedTimestamp - } - } - - @Override - void store(final Writer writer, final String comments) throws IOException { - super.store(new StripCommentsWithTimestampBufferedWriter(writer), comments) - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.groovy deleted file mode 100644 index 4734fedd1..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.groovy +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import groovy.util.logging.Slf4j -import org.gradle.api.Project -import org.gradle.api.artifacts.ResolvedDependency - -@Slf4j -class DefaultDependencyFilter extends AbstractDependencyFilter { - - DefaultDependencyFilter(Project project) { - super(project) - } - - @Override - protected void resolve(Set dependencies, - Set includedDependencies, - Set excludedDependencies) { - dependencies.each { - if (isIncluded(it) ? includedDependencies.add(it) : excludedDependencies.add(it)) { - resolve(it.children, includedDependencies, excludedDependencies) - } - } - } - -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.groovy deleted file mode 100644 index a2248e6a2..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.groovy +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2012 the original author or authors. - * - * 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 com.github.jengelman.gradle.plugins.shadow.internal - -import org.apache.tools.zip.Zip64Mode -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.UncheckedIOException - -class DefaultZipCompressor implements ZipCompressor { - private final int entryCompressionMethod - private final Zip64Mode zip64Mode - - DefaultZipCompressor(boolean allowZip64Mode, int entryCompressionMethod) { - this.entryCompressionMethod = entryCompressionMethod - zip64Mode = allowZip64Mode ? Zip64Mode.AsNeeded : Zip64Mode.Never - } - - @Override - ZipOutputStream createArchiveOutputStream(File destination) { - try { - ZipOutputStream zipOutputStream = new ZipOutputStream(destination) - zipOutputStream.setUseZip64(zip64Mode) - zipOutputStream.setMethod(entryCompressionMethod) - return zipOutputStream - } catch (Exception e) { - String message = String.format("Unable to create ZIP output stream for file %s.", destination) - throw new UncheckedIOException(message, e) - } - } - -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.groovy deleted file mode 100644 index e8f6d4644..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.groovy +++ /dev/null @@ -1,75 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import org.gradle.api.artifacts.Dependency -import org.gradle.api.artifacts.ResolvedDependency -import org.gradle.api.file.FileCollection -import org.gradle.api.specs.Spec - -interface DependencyFilter { - - /** - * Resolve a FileCollection against the include/exclude rules in the filter - * @param configuration - * @return - */ - FileCollection resolve(FileCollection configuration) - - /** - * Resolve all FileCollections against the include/exclude ruels in the filter and combine the results - * @param configurations - * @return - */ - FileCollection resolve(Collection configurations) - - /** - * Exclude dependencies that match the provided spec. - * - * @param spec - * @return - */ - DependencyFilter exclude(Spec spec) - - /** - * Include dependencies that match the provided spec. - * - * @param spec - * @return - */ - DependencyFilter include(Spec spec) - - /** - * Create a spec that matches the provided project notation on group, name, and version - * @param notation - * @return - */ - Spec project(Map notation) - - /** - * Create a spec that matches the default configuration for the provided project path on group, name, and version - * - * @param notation - * @return - */ - Spec project(String notation) - - /** - * Create a spec that matches dependencies using the provided notation on group, name, and version - * @param notation - * @return - */ - Spec dependency(Object notation) - - /** - * Create a spec that matches the provided dependency on group, name, and version - * @param dependency - * @return - */ - Spec dependency(Dependency dependency) - - /** - * Create a spec that matches the provided closure - * @param spec - * @return - */ - Spec dependency(Closure spec) -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/GradleVersionUtil.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/GradleVersionUtil.groovy deleted file mode 100644 index 3bc6f0563..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/GradleVersionUtil.groovy +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.internal.file.copy.CopySpecInternal -import org.gradle.api.tasks.bundling.Jar -import org.gradle.api.tasks.bundling.ZipEntryCompression -import org.gradle.api.tasks.util.PatternSet - -class GradleVersionUtil { - - static PatternSet getRootPatternSet(CopySpecInternal mainSpec) { - return mainSpec.buildRootResolver().getPatternSet() - } - - static ZipCompressor getInternalCompressor(ZipEntryCompression entryCompression, Jar jar) { - switch (entryCompression) { - case ZipEntryCompression.DEFLATED: - return new DefaultZipCompressor(jar.zip64, ZipOutputStream.DEFLATED) - case ZipEntryCompression.STORED: - return new DefaultZipCompressor(jar.zip64, ZipOutputStream.STORED) - default: - throw new IllegalArgumentException(String.format("Unknown Compression type %s", entryCompression)) - } - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.groovy deleted file mode 100644 index 2affa9fbe..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.groovy +++ /dev/null @@ -1,19 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.JavaExec -import org.gradle.api.tasks.TaskAction - -class JavaJarExec extends JavaExec { - - @InputFile - File jarFile - - @Override - @TaskAction - void exec() { - List allArgs = [getJarFile().path] + getArgs() - setArgs(allArgs) - super.exec() - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.groovy deleted file mode 100644 index 4853c3a77..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.groovy +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import groovy.util.logging.Slf4j -import org.gradle.api.Project -import org.gradle.api.artifacts.ResolvedDependency - -@Slf4j -class MinimizeDependencyFilter extends AbstractDependencyFilter { - - MinimizeDependencyFilter(Project project) { - super(project) - } - - @Override - protected void resolve(Set dependencies, - Set includedDependencies, - Set excludedDependencies) { - - dependencies.each { - if (isIncluded(it) && !isParentExcluded(excludedDependencies, it) ? includedDependencies.add(it) : excludedDependencies.add(it)) { - resolve(it.children, includedDependencies, excludedDependencies) - } - } - } - - private static boolean isParentExcluded(Set excludedDependencies, ResolvedDependency dependency) { - excludedDependencies.any { dependency.parents.contains(it) } - } -} \ No newline at end of file diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/RelocationUtil.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/RelocationUtil.groovy deleted file mode 100644 index f94ab89ed..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/RelocationUtil.groovy +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import java.util.jar.JarFile - -class RelocationUtil { - - static void configureRelocation(ShadowJar target, String prefix) { - def packages = [] as Set - target.configurations.each { configuration -> - configuration.files.each { jar -> - JarFile jf = new JarFile(jar) - jf.entries().each { entry -> - if (entry.name.endsWith(".class") && entry.name != "module-info.class") { - packages << entry.name[0..entry.name.lastIndexOf('/') - 1].replaceAll('/', '.') - } - } - jf.close() - } - } - packages.each { - target.relocate(it, "${prefix}.${it}") - } - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.groovy deleted file mode 100644 index fcfbe6ea1..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.groovy +++ /dev/null @@ -1,85 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.Dependency -import org.gradle.api.artifacts.ProjectDependency -import org.gradle.api.artifacts.SelfResolvingDependency -import org.gradle.api.file.FileCollection -import org.gradle.api.tasks.InputFiles -import org.vafer.jdependency.Clazz -import org.vafer.jdependency.Clazzpath -import org.vafer.jdependency.ClazzpathUnit - -/** Tracks unused classes in the project classpath. */ -class UnusedTracker { - private final FileCollection toMinimize - private final List projectUnits - private final Clazzpath cp = new Clazzpath() - - private UnusedTracker(Iterable classDirs, FileCollection classJars, FileCollection toMinimize) { - this.toMinimize = toMinimize - projectUnits = classDirs.collect { cp.addClazzpathUnit(it) } - projectUnits.addAll(classJars.collect { cp.addClazzpathUnit(it) }) - } - - Set findUnused() { - Set unused = cp.clazzes - for (cpu in projectUnits) { - unused.removeAll(cpu.clazzes) - unused.removeAll(cpu.transitiveDependencies) - } - return unused.collect { it.name }.toSet() - } - - void addDependency(File jarOrDir) { - if (toMinimize.contains(jarOrDir)) { - cp.addClazzpathUnit(jarOrDir) - } - } - - static UnusedTracker forProject(FileCollection apiJars, Iterable sourceSetsClassesDirs, FileCollection toMinimize) { - return new UnusedTracker(sourceSetsClassesDirs, apiJars, toMinimize) - } - - @InputFiles - FileCollection getToMinimize() { - return toMinimize - } - - private static boolean isProjectDependencyFile(File file, Dependency dep) { - def fileName = file.name - def dependencyName = dep.name - - return (fileName == "${dependencyName}.jar") || - (fileName.startsWith("${dependencyName}-") && fileName.endsWith('.jar')) - } - - private static void addJar(Configuration config, Dependency dep, List result) { - def file = config.find { isProjectDependencyFile(it, dep) } as File - if (file != null) { - result.add(file) - } - } - - static FileCollection getApiJarsFromProject(Project project) { - def apiDependencies = project.configurations.asMap['api']?.dependencies ?: null - if (apiDependencies == null) return project.files() - - def runtimeConfiguration = project.configurations.asMap['runtimeClasspath'] ?: project.configurations.runtime - def apiJars = new LinkedList() - apiDependencies.each { dep -> - if (dep instanceof ProjectDependency) { - apiJars.addAll(getApiJarsFromProject(dep.dependencyProject)) - addJar(runtimeConfiguration, dep, apiJars) - } else if (dep instanceof SelfResolvingDependency) { - apiJars.addAll(dep.resolve()) - } else { - addJar(runtimeConfiguration, dep, apiJars) - apiJars.add(runtimeConfiguration.find { it.name.startsWith("${dep.name}-") } as File) - } - } - - return project.files(apiJars) - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.groovy deleted file mode 100644 index 86ed6d5e6..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.groovy +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2012 the original author or authors. - * - * 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 com.github.jengelman.gradle.plugins.shadow.internal - -import org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory - -interface ZipCompressor extends ArchiveOutputStreamFactory { -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.groovy deleted file mode 100644 index e3e69a7ef..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.groovy +++ /dev/null @@ -1,16 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.legacy - -import org.gradle.api.Plugin -import org.gradle.api.Project - -/** - * Empty plugin to still have the com.github.johnrengelman.shadow plugin applied. - * - * This allows older build logic to keep on working as if that old plugin ID was applied. - */ -class LegacyShadowPlugin implements Plugin { - @Override - void apply(Project target) { - // Do nothing - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.groovy deleted file mode 100644 index 966e3f524..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.groovy +++ /dev/null @@ -1,17 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.relocation - -import java.lang.annotation.ElementType -import java.lang.annotation.Retention -import java.lang.annotation.RetentionPolicy -import java.lang.annotation.Target - -/** - * Marks that a given instance of {@link Relocator} is is compatible with the Gradle build cache. - * In other words, it has its appropriate inputs annotated so that Gradle can consider them when - * determining the cache key. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@interface CacheableRelocator { - -} \ No newline at end of file diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.groovy deleted file mode 100644 index 9e0e97e6f..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.groovy +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.relocation - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import groovy.transform.Canonical -import groovy.transform.builder.Builder - -@Canonical -@Builder -class RelocateClassContext { - - String className - ShadowStats stats - -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.groovy deleted file mode 100644 index 34be58b4f..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.groovy +++ /dev/null @@ -1,13 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.relocation - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import groovy.transform.Canonical -import groovy.transform.builder.Builder - -@Canonical -@Builder -class RelocatePathContext { - - String path - ShadowStats stats -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.groovy deleted file mode 100644 index d9cb1d48f..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.groovy +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.relocation - -/** - * Modified from org.apache.maven.plugins.shade.relocation.Relocator.java - * - * @author Jason van Zyl - * @author John Engelman - */ -interface Relocator { - String ROLE = Relocator.class.getName() - - boolean canRelocatePath(String path) - - String relocatePath(RelocatePathContext context) - - boolean canRelocateClass(String className) - - String relocateClass(RelocateClassContext context) - - String applyToSourceContent(String sourceContent) -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.groovy deleted file mode 100644 index 4f4ec7688..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.groovy +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.relocation - -import org.codehaus.plexus.util.SelectorUtils -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional - -import java.util.regex.Pattern - -/** - * Modified from org.apache.maven.plugins.shade.relocation.SimpleRelocator.java - * - * @author Jason van Zyl - * @author Mauro Talevi - * @author John Engelman - */ -@CacheableRelocator -class SimpleRelocator implements Relocator { - - private final String pattern - - private final String pathPattern - - private final String shadedPattern - - private final String shadedPathPattern - - private final Set includes - - private final Set excludes - - private final boolean rawString - - SimpleRelocator() { - - } - - SimpleRelocator(String patt, String shadedPattern, List includes, List excludes) { - this(patt, shadedPattern, includes, excludes, false) - } - - SimpleRelocator(String patt, String shadedPattern, List includes, List excludes, - boolean rawString) { - this.rawString = rawString - - if (rawString) { - this.pathPattern = patt - this.shadedPathPattern = shadedPattern - - this.pattern = null // not used for raw string relocator - this.shadedPattern = null // not used for raw string relocator - } else { - if (patt == null) { - this.pattern = "" - this.pathPattern = "" - } else { - this.pattern = patt.replace('/', '.') - this.pathPattern = patt.replace('.', '/') - } - - if (shadedPattern != null) { - this.shadedPattern = shadedPattern.replace('/', '.') - this.shadedPathPattern = shadedPattern.replace('.', '/') - } else { - this.shadedPattern = "hidden." + this.pattern - this.shadedPathPattern = "hidden/" + this.pathPattern - } - } - - this.includes = normalizePatterns(includes) - this.excludes = normalizePatterns(excludes) - } - - SimpleRelocator include(String pattern) { - this.includes.addAll normalizePatterns([pattern]) - return this - } - - SimpleRelocator exclude(String pattern) { - this.excludes.addAll normalizePatterns([pattern]) - return this - } - - private static Set normalizePatterns(Collection patterns) { - Set normalized = null - - if (patterns != null && !patterns.isEmpty()) { - normalized = new LinkedHashSet() - - for (String pattern : patterns) { - // Regex patterns don't need to be normalized and stay as is - if (pattern.startsWith(SelectorUtils.REGEX_HANDLER_PREFIX)) { - normalized.add(pattern) - continue - } - - String classPattern = pattern.replace('.', '/') - - normalized.add(classPattern) - - if (classPattern.endsWith("/*")) { - String packagePattern = classPattern.substring(0, classPattern.lastIndexOf('/')) - normalized.add(packagePattern) - } - } - } - - return normalized ?: [] - } - - private boolean isIncluded(String path) { - if (includes != null && !includes.isEmpty()) { - for (String include : includes) { - if (SelectorUtils.matchPath(include, path, '/', true)) { - return true - } - } - return false - } - return true - } - - private boolean isExcluded(String path) { - if (excludes != null && !excludes.isEmpty()) { - for (String exclude : excludes) { - if (SelectorUtils.matchPath(exclude, path, '/', true)) { - return true - } - } - } - return false - } - - @Override - boolean canRelocatePath(String path) { - if (rawString) { - return Pattern.compile(pathPattern).matcher(path).find() - } - - // If string is too short - no need to perform expensive string operations - if (path.length() < pathPattern.length()) { - return false - } - - if (path.endsWith(".class")) { - // Safeguard against strings containing only ".class" - if (path.length() == 6) { - return false - } - path = path.substring(0, path.length() - 6) - } - - // Allow for annoying option of an extra / on the front of a path. See MSHADE-119 comes from getClass().getResource("/a/b/c.properties"). - boolean pathStartsWithPattern = - path.charAt(0) == '/' ? path.startsWith(pathPattern, 1) : path.startsWith(pathPattern) - if (pathStartsWithPattern) { - return isIncluded(path) && !isExcluded(path) - } - return false - } - - @Override - boolean canRelocateClass(String className) { - return !rawString && - className.indexOf('/') < 0 && - canRelocatePath(className.replace('.', '/')) - } - - @Override - String relocatePath(RelocatePathContext context) { - String path = context.path - context.stats.relocate(pathPattern, shadedPathPattern) - if (rawString) { - return path.replaceAll(pathPattern, shadedPathPattern) - } else { - return path.replaceFirst(pathPattern, shadedPathPattern) - } - } - - @Override - String relocateClass(RelocateClassContext context) { - String clazz = context.className - context.stats.relocate(pathPattern, shadedPathPattern) - return clazz.replaceFirst(pattern, shadedPattern) - } - - @Override - String applyToSourceContent(String sourceContent) { - if (rawString) { - return sourceContent - } else { - return sourceContent.replaceAll("\\b" + pattern, shadedPattern) - } - } - - @Input - @Optional - String getPattern() { - return pattern - } - - @Input - String getPathPattern() { - return pathPattern - } - - @Input - @Optional - String getShadedPattern() { - return shadedPattern - } - - @Input - String getShadedPathPattern() { - return shadedPathPattern - } - - @Input - Set getIncludes() { - return includes - } - - @Input - Set getExcludes() { - return excludes - } - - @Input - boolean getRawString() { - return rawString - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.groovy deleted file mode 100644 index 7f3ad7d37..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.groovy +++ /dev/null @@ -1,104 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks - -import org.gradle.api.Action -import org.gradle.api.Project -import org.gradle.api.internal.file.FileResolver -import org.gradle.api.java.archives.Attributes -import org.gradle.api.java.archives.Manifest -import org.gradle.api.java.archives.ManifestException -import org.gradle.api.java.archives.ManifestMergeSpec -import org.gradle.api.java.archives.internal.DefaultManifest -import org.gradle.api.java.archives.internal.DefaultManifestMergeSpec - -class DefaultInheritManifest implements InheritManifest { - - private List inheritMergeSpecs = [] - - private final transient Project project - - private final FileResolver fileResolver - - private final Manifest internalManifest - - DefaultInheritManifest(Project project, FileResolver fileResolver) { - this.project = project - this.internalManifest = new DefaultManifest(fileResolver) - this.fileResolver = fileResolver - } - - @Override - InheritManifest inheritFrom(Object... inheritPaths) { - inheritFrom(inheritPaths, null) - return this - } - - @Override - InheritManifest inheritFrom(Object inheritPaths, Closure closure) { - DefaultManifestMergeSpec mergeSpec = new DefaultManifestMergeSpec() - mergeSpec.from(inheritPaths) - inheritMergeSpecs.add(mergeSpec) - project.configure(mergeSpec, closure) - return this - } - - @Override - Attributes getAttributes() { - return internalManifest.getAttributes() - } - - @Override - Map getSections() { - return internalManifest.getSections() - } - - @Override - Manifest attributes(Map map) throws ManifestException { - internalManifest.attributes(map) - return this - } - - @Override - Manifest attributes(Map map, String s) throws ManifestException { - internalManifest.attributes(map, s) - return this - } - - @Override - DefaultManifest getEffectiveManifest() { - DefaultManifest base = new DefaultManifest(fileResolver) - inheritMergeSpecs.each { - base = it.merge(base, fileResolver) - } - base.from internalManifest - return base.getEffectiveManifest() - } - - Manifest writeTo(Writer writer) { - this.getEffectiveManifest().writeTo((Object) writer) - return this - } - - @Override - Manifest writeTo(Object o) { - this.getEffectiveManifest().writeTo(o) - return this - } - - @Override - Manifest from(Object... objects) { - internalManifest.from(objects) - return this - } - - @Override - Manifest from(Object o, Closure closure) { - internalManifest.from(o, closure) - return this - } - - @Override - Manifest from(Object o, Action action) { - internalManifest.from(o, action) - return this - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.groovy deleted file mode 100644 index 1cfed3c4c..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.groovy +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks - -import org.gradle.api.java.archives.Manifest - -interface InheritManifest extends Manifest { - - InheritManifest inheritFrom(Object... inheritPaths) - - InheritManifest inheritFrom(inheritPaths, Closure closure) -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.groovy deleted file mode 100644 index 4a103ffe0..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.groovy +++ /dev/null @@ -1,17 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks - -import org.codehaus.groovy.reflection.ReflectionUtils -import org.gradle.api.DefaultTask -import org.gradle.api.tasks.TaskAction - -class KnowsTask extends DefaultTask { - - public static final String NAME = "knows" - public static final String DESC = "Do you know who knows?" - - @TaskAction - def knows() { - println "\nNo, The Shadow Knows...." - println ReflectionUtils.getCallingClass(0).getResourceAsStream("/shadowBanner.txt").text - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.groovy deleted file mode 100644 index 128dc3cce..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.groovy +++ /dev/null @@ -1,545 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.impl.RelocatorRemapper -import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker -import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer -import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext -import groovy.util.logging.Slf4j -import org.apache.commons.io.FilenameUtils -import org.apache.commons.io.IOUtils -import org.apache.tools.zip.UnixStat -import org.apache.tools.zip.Zip64RequiredException -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipFile -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.Action -import org.gradle.api.GradleException -import org.gradle.api.UncheckedIOException -import org.gradle.api.file.FileCopyDetails -import org.gradle.api.file.FilePermissions -import org.gradle.api.file.FileTreeElement -import org.gradle.api.file.RelativePath -import org.gradle.api.internal.DocumentationRegistry -import org.gradle.api.internal.file.CopyActionProcessingStreamAction -import org.gradle.api.internal.file.DefaultFilePermissions -import org.gradle.api.internal.file.DefaultFileTreeElement -import org.gradle.api.internal.file.copy.CopyAction -import org.gradle.api.internal.file.copy.CopyActionProcessingStream -import org.gradle.api.internal.file.copy.FileCopyDetailsInternal -import org.gradle.api.specs.Spec -import org.gradle.api.tasks.WorkResult -import org.gradle.api.tasks.WorkResults -import org.gradle.api.tasks.bundling.Zip -import org.gradle.api.tasks.util.PatternSet -import org.gradle.internal.UncheckedException -import org.objectweb.asm.ClassReader -import org.objectweb.asm.ClassVisitor -import org.objectweb.asm.ClassWriter -import org.objectweb.asm.commons.ClassRemapper - -import java.util.zip.ZipException - -@Slf4j -class ShadowCopyAction implements CopyAction { - static final long CONSTANT_TIME_FOR_ZIP_ENTRIES = (new GregorianCalendar(1980, 1, 1, 0, 0, 0)).getTimeInMillis() - - private final File zipFile - private final ZipCompressor compressor - private final DocumentationRegistry documentationRegistry - private final List transformers - private final List relocators - private final PatternSet patternSet - private final ShadowStats stats - private final String encoding - private final boolean preserveFileTimestamps - private final boolean minimizeJar - private final UnusedTracker unusedTracker - - ShadowCopyAction(File zipFile, ZipCompressor compressor, DocumentationRegistry documentationRegistry, - String encoding, List transformers, List relocators, - PatternSet patternSet, ShadowStats stats, - boolean preserveFileTimestamps, boolean minimizeJar, UnusedTracker unusedTracker) { - - this.zipFile = zipFile - this.compressor = compressor - this.documentationRegistry = documentationRegistry - this.transformers = transformers - this.relocators = relocators - this.patternSet = patternSet - this.stats = stats - this.encoding = encoding - this.preserveFileTimestamps = preserveFileTimestamps - this.minimizeJar = minimizeJar - this.unusedTracker = unusedTracker - } - - @Override - WorkResult execute(CopyActionProcessingStream stream) { - Set unusedClasses - if (minimizeJar) { - stream.process(new BaseStreamAction() { - @Override - void visitFile(FileCopyDetails fileDetails) { - // All project sources are already present, we just need - // to deal with JAR dependencies. - if (isArchive(fileDetails)) { - unusedTracker.addDependency(fileDetails.file) - } - } - }) - unusedClasses = unusedTracker.findUnused() - } else { - unusedClasses = Collections.emptySet() - } - - ZipOutputStream zipOutStr - - try { - zipOutStr = compressor.createArchiveOutputStream(zipFile) - } catch (Exception e) { - throw new GradleException("Could not create ZIP '${zipFile.toString()}'", e) - } - - try { - withResource(zipOutStr, new Action() { - void execute(ZipOutputStream outputStream) { - try { - stream.process(new StreamAction(outputStream, encoding, transformers, relocators, patternSet, - unusedClasses, stats)) - processTransformers(outputStream) - } catch (Exception e) { - log.error('ex', e) - //TODO this should not be rethrown - throw e - } - } - }) - } catch (UncheckedIOException e) { - if (e.cause instanceof Zip64RequiredException) { - throw new Zip64RequiredException( - String.format("%s\n\nTo build this archive, please enable the zip64 extension.\nSee: %s", - e.cause.message, documentationRegistry.getDslRefForProperty(Zip, "zip64")) - ) - } - } - return WorkResults.didWork(true) - } - - private void processTransformers(ZipOutputStream stream) { - transformers.each { Transformer transformer -> - if (transformer.hasTransformedResource()) { - transformer.modifyOutputStream(stream, preserveFileTimestamps) - } - } - } - - private long getArchiveTimeFor(long timestamp) { - return preserveFileTimestamps ? timestamp : CONSTANT_TIME_FOR_ZIP_ENTRIES - } - - private ZipEntry setArchiveTimes(ZipEntry zipEntry) { - if (!preserveFileTimestamps) { - zipEntry.setTime(CONSTANT_TIME_FOR_ZIP_ENTRIES) - } - return zipEntry - } - - private static void withResource(T resource, Action action) { - try { - action.execute(resource) - } catch(Throwable t) { - try { - resource.close() - } catch (IOException e) { - log.warn("Could not close resource $resource", e) - } - throw UncheckedException.throwAsUncheckedException(t) - } - - try { - resource.close() - } catch (IOException e) { - throw new UncheckedIOException(e) - } - } - - abstract class BaseStreamAction implements CopyActionProcessingStreamAction { - protected boolean isArchive(FileCopyDetails fileDetails) { - return fileDetails.relativePath.pathString.endsWith('.jar') - } - - protected boolean isClass(FileCopyDetails fileDetails) { - return FilenameUtils.getExtension(fileDetails.path) == 'class' - } - - @Override - void processFile(FileCopyDetailsInternal details) { - if (details.directory) { - visitDir(details) - } else { - visitFile(details) - } - } - - protected void visitDir(FileCopyDetails dirDetails) {} - - protected abstract void visitFile(FileCopyDetails fileDetails) - } - - private class StreamAction extends BaseStreamAction { - - private final ZipOutputStream zipOutStr - private final List transformers - private final List relocators - private final RelocatorRemapper remapper - private final PatternSet patternSet - private final Set unused - private final ShadowStats stats - - private Set visitedFiles = new HashSet() - - StreamAction(ZipOutputStream zipOutStr, String encoding, List transformers, - List relocators, PatternSet patternSet, Set unused, - ShadowStats stats) { - this.zipOutStr = zipOutStr - this.transformers = transformers - this.relocators = relocators - this.remapper = new RelocatorRemapper(relocators, stats) - this.patternSet = patternSet - this.unused = unused - this.stats = stats - if(encoding != null) { - this.zipOutStr.setEncoding(encoding) - } - } - - private boolean recordVisit(RelativePath path) { - return visitedFiles.add(path.pathString) - } - - @Override - void visitFile(FileCopyDetails fileDetails) { - if (!isArchive(fileDetails)) { - try { - boolean isClass = isClass(fileDetails) - if (!remapper.hasRelocators() || !isClass) { - if (!isTransformable(fileDetails)) { - String mappedPath = remapper.map(fileDetails.relativePath.pathString) - ZipEntry archiveEntry = new ZipEntry(mappedPath) - archiveEntry.setTime(getArchiveTimeFor(fileDetails.lastModified)) - archiveEntry.unixMode = (UnixStat.FILE_FLAG | fileDetails.permissions.toUnixNumeric()) - zipOutStr.putNextEntry(archiveEntry) - fileDetails.copyTo(zipOutStr) - zipOutStr.closeEntry() - } else { - transform(fileDetails) - } - } else if (isClass && !isUnused(fileDetails.path)) { - remapClass(fileDetails) - } - recordVisit(fileDetails.relativePath) - } catch (Exception e) { - throw new GradleException(String.format("Could not add %s to ZIP '%s'.", fileDetails, zipFile), e) - } - } else { - processArchive(fileDetails) - } - } - - private void processArchive(FileCopyDetails fileDetails) { - stats.startJar() - ZipFile archive = new ZipFile(fileDetails.file) - try { - List archiveElements = archive.entries.collect { - new ArchiveFileTreeElement(new RelativeArchivePath(it)) - } - Spec patternSpec = patternSet.getAsSpec() - List filteredArchiveElements = archiveElements.findAll { ArchiveFileTreeElement archiveElement -> - patternSpec.isSatisfiedBy(archiveElement.asFileTreeElement()) - } - filteredArchiveElements.each { ArchiveFileTreeElement archiveElement -> - if (archiveElement.relativePath.file) { - visitArchiveFile(archiveElement, archive) - } - } - } finally { - archive.close() - } - stats.finishJar() - } - - private void visitArchiveDirectory(RelativeArchivePath archiveDir) { - if (recordVisit(archiveDir)) { - zipOutStr.putNextEntry(archiveDir.entry) - zipOutStr.closeEntry() - } - } - - private void visitArchiveFile(ArchiveFileTreeElement archiveFile, ZipFile archive) { - def archiveFilePath = archiveFile.relativePath - if (archiveFile.classFile || !isTransformable(archiveFile)) { - if (recordVisit(archiveFilePath) && !isUnused(archiveFilePath.entry.name)) { - if (!remapper.hasRelocators() || !archiveFile.classFile) { - copyArchiveEntry(archiveFilePath, archive) - } else { - remapClass(archiveFilePath, archive) - } - } - } else { - transform(archiveFile, archive) - } - } - - private void addParentDirectories(RelativeArchivePath file) { - if (file) { - addParentDirectories(file.parent) - if (!file.file) { - visitArchiveDirectory(file) - } - } - } - - private boolean isUnused(String classPath) { - final String className = FilenameUtils.removeExtension(classPath) - .replace('/' as char, '.' as char) - final boolean result = unused.contains(className) - if (result) { - log.debug("Dropping unused class: $className") - } - return result - } - - private void remapClass(RelativeArchivePath file, ZipFile archive) { - if (file.classFile) { - ZipEntry zipEntry = setArchiveTimes(new ZipEntry(remapper.mapPath(file) + '.class')) - addParentDirectories(new RelativeArchivePath(zipEntry)) - remapClass(archive.getInputStream(file.entry), file.pathString, file.entry.time) - } - } - - private void remapClass(FileCopyDetails fileCopyDetails) { - if (FilenameUtils.getExtension(fileCopyDetails.name) == 'class') { - InputStream is = fileCopyDetails.file.newInputStream() - try { - remapClass(is, fileCopyDetails.path, fileCopyDetails.lastModified) - } finally { - is.close() - } - } - } - - /** - * Applies remapping to the given class with the specified relocation path. The remapped class is then written - * to the zip file. classInputStream is closed automatically to prevent future file leaks. - * See #364 and #408. - */ - private void remapClass(InputStream classInputStream, String path, long lastModified) { - InputStream is = classInputStream - ClassReader cr = new ClassReader(is) - - // We don't pass the ClassReader here. This forces the ClassWriter to rebuild the constant pool. - // Copying the original constant pool should be avoided because it would keep references - // to the original class names. This is not a problem at runtime (because these entries in the - // constant pool are never used), but confuses some tools such as Felix' maven-bundle-plugin - // that use the constant pool to determine the dependencies of a class. - ClassWriter cw = new ClassWriter(0) - - ClassVisitor cv = new ClassRemapper(cw, remapper) - - try { - cr.accept(cv, ClassReader.EXPAND_FRAMES) - } catch (Throwable ise) { - throw new GradleException("Error in ASM processing class " + path, ise) - } finally { - is.close() - } - - byte[] renamedClass = cw.toByteArray() - - // Temporarily remove the multi-release prefix. - String multiReleasePrefix = path.find("^META-INF/versions/\\d+/") ?: "" - path = path.replace(multiReleasePrefix, "") - String mappedName = multiReleasePrefix + remapper.mapPath(path) - - InputStream bis = new ByteArrayInputStream(renamedClass) - try { - // Now we put it back on so the class file is written out with the right extension. - ZipEntry archiveEntry = new ZipEntry(mappedName + ".class") - archiveEntry.setTime(getArchiveTimeFor(lastModified)) - zipOutStr.putNextEntry(archiveEntry) - IOUtils.copyLarge(bis, zipOutStr) - zipOutStr.closeEntry() - } catch (ZipException ignored) { - log.warn("We have a duplicate " + mappedName + " in source project") - } finally { - bis.close() - } - } - - private void copyArchiveEntry(RelativeArchivePath archiveFile, ZipFile archive) { - String mappedPath = remapper.map(archiveFile.entry.name) - ZipEntry entry = new ZipEntry(mappedPath) - entry.setTime(getArchiveTimeFor(archiveFile.entry.time)) - RelativeArchivePath mappedFile = new RelativeArchivePath(entry) - addParentDirectories(mappedFile) - zipOutStr.putNextEntry(mappedFile.entry) - InputStream is = archive.getInputStream(archiveFile.entry) - try { - IOUtils.copyLarge(is, zipOutStr) - } finally { - is.close() - } - zipOutStr.closeEntry() - } - - @Override - protected void visitDir(FileCopyDetails dirDetails) { - try { - // Trailing slash in name indicates that entry is a directory - String path = dirDetails.relativePath.pathString + '/' - ZipEntry archiveEntry = new ZipEntry(path) - archiveEntry.setTime(getArchiveTimeFor(dirDetails.lastModified)) - archiveEntry.unixMode = (UnixStat.DIR_FLAG | dirDetails.permissions.toUnixNumeric()) - zipOutStr.putNextEntry(archiveEntry) - zipOutStr.closeEntry() - recordVisit(dirDetails.relativePath) - } catch (Exception e) { - throw new GradleException(String.format("Could not add %s to ZIP '%s'.", dirDetails, zipFile), e) - } - } - - private void transform(ArchiveFileTreeElement element, ZipFile archive) { - transformAndClose(element, archive.getInputStream(element.relativePath.entry)) - } - - private void transform(FileCopyDetails details) { - transformAndClose(details, details.file.newInputStream()) - } - - private void transformAndClose(FileTreeElement element, InputStream is) { - try { - String mappedPath = remapper.map(element.relativePath.pathString) - transformers.find { it.canTransformResource(element) }.transform( - TransformerContext.builder() - .path(mappedPath) - .is(is) - .relocators(relocators) - .stats(stats) - .build() - ) - } finally { - is.close() - } - } - - private boolean isTransformable(FileTreeElement element) { - return transformers.any { it.canTransformResource(element) } - } - - } - - class RelativeArchivePath extends RelativePath { - - ZipEntry entry - - RelativeArchivePath(ZipEntry entry) { - super(!entry.directory, entry.name.split('/')) - this.entry = entry - } - - boolean isClassFile() { - return lastName.endsWith('.class') - } - - @Override - RelativeArchivePath getParent() { - if (!segments || segments.length == 1) { - return null - } else { - //Parent is always a directory so add / to the end of the path - String path = segments[0..-2].join('/') + '/' - return new RelativeArchivePath(setArchiveTimes(new ZipEntry(path))) - } - } - } - - class ArchiveFileTreeElement implements FileTreeElement { - - private final RelativeArchivePath archivePath - - ArchiveFileTreeElement(RelativeArchivePath archivePath) { - this.archivePath = archivePath - } - - boolean isClassFile() { - return archivePath.classFile - } - - @Override - File getFile() { - return null - } - - @Override - boolean isDirectory() { - return archivePath.entry.directory - } - - @Override - long getLastModified() { - return archivePath.entry.lastModifiedDate.time - } - - @Override - long getSize() { - return archivePath.entry.size - } - - @Override - InputStream open() { - return null - } - - @Override - void copyTo(OutputStream outputStream) { - - } - - @Override - boolean copyTo(File file) { - return false - } - - @Override - String getName() { - return archivePath.pathString - } - - @Override - String getPath() { - return archivePath.lastName - } - - @Override - RelativeArchivePath getRelativePath() { - return archivePath - } - - @Override - int getMode() { - return archivePath.entry.unixMode - } - - @Override - FilePermissions getPermissions() { - return new DefaultFilePermissions(getMode()) - } - - FileTreeElement asFileTreeElement() { - return new DefaultFileTreeElement(null, new RelativePath(!isDirectory(), archivePath.segments), null, null) - } - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.java b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.java deleted file mode 100644 index dc4cfe9e3..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.java +++ /dev/null @@ -1,450 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks; - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats; -import com.github.jengelman.gradle.plugins.shadow.internal.*; -import com.github.jengelman.gradle.plugins.shadow.relocation.CacheableRelocator; -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator; -import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator; -import com.github.jengelman.gradle.plugins.shadow.transformers.*; -import org.gradle.api.Action; -import org.gradle.api.file.ConfigurableFileCollection; -import org.gradle.api.file.DuplicatesStrategy; -import org.gradle.api.file.FileCollection; -import org.gradle.api.internal.DocumentationRegistry; -import org.gradle.api.internal.file.FileResolver; -import org.gradle.api.internal.file.copy.CopyAction; -import org.gradle.api.tasks.*; -import org.gradle.api.tasks.bundling.Jar; -import org.gradle.api.tasks.util.PatternSet; -import org.jetbrains.annotations.NotNull; - -import java.io.File; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Callable; - -@CacheableTask -public class ShadowJar extends Jar implements ShadowSpec { - - private List transformers; - private List relocators; - private List configurations; - private transient DependencyFilter dependencyFilter; - private boolean enableRelocation; - private String relocationPrefix = "shadow"; - private boolean minimizeJar; - private final transient DependencyFilter dependencyFilterForMinimize; - private FileCollection toMinimize; - private FileCollection apiJars; - private FileCollection sourceSetsClassesDirs; - - private final ShadowStats shadowStats = new ShadowStats(); - - private final ConfigurableFileCollection includedDependencies = getProject().files(new Callable() { - - @Override - public FileCollection call() { - return dependencyFilter.resolve(configurations); - } - }); - - public ShadowJar() { - super(); - setDuplicatesStrategy(DuplicatesStrategy.INCLUDE); //shadow filters out files later. This was the default behavior in Gradle < 6.x - dependencyFilter = new DefaultDependencyFilter(getProject()); - dependencyFilterForMinimize = new MinimizeDependencyFilter(getProject()); - setManifest(new DefaultInheritManifest(getProject(), getServices().get(FileResolver.class))); - transformers = new ArrayList<>(); - relocators = new ArrayList<>(); - configurations = new ArrayList<>(); - - this.getInputs().property("minimize", (Callable) () -> minimizeJar); - this.getOutputs().doNotCacheIf("Has one or more transforms or relocators that are not cacheable", task -> { - for (Transformer transformer : transformers) { - if (!isCacheableTransform(transformer.getClass())) { - return true; - } - } - for (Relocator relocator : relocators) { - if (!isCacheableRelocator(relocator.getClass())) { - return true; - } - } - return false; - }); - } - - @Override - public ShadowJar minimize() { - minimizeJar = true; - return this; - } - - @Override - public ShadowJar minimize(Action c) { - minimize(); - if (c != null) { - c.execute(dependencyFilterForMinimize); - } - return this; - } - - @Override - @Internal - public ShadowStats getStats() { - return shadowStats; - } - - @Override - public InheritManifest getManifest() { - return (InheritManifest) super.getManifest(); - } - - @Override - @NotNull - protected CopyAction createCopyAction() { - DocumentationRegistry documentationRegistry = getServices().get(DocumentationRegistry.class); - final UnusedTracker unusedTracker = minimizeJar ? UnusedTracker.forProject(getApiJars(), getSourceSetsClassesDirs().getFiles(), getToMinimize()) : null; - return new ShadowCopyAction(getArchiveFile().get().getAsFile(), getInternalCompressor(), documentationRegistry, - this.getMetadataCharset(), transformers, relocators, getRootPatternSet(), shadowStats, - isPreserveFileTimestamps(), minimizeJar, unusedTracker); - } - - @Classpath - FileCollection getToMinimize() { - if (toMinimize == null) { - toMinimize = minimizeJar - ? dependencyFilterForMinimize.resolve(configurations).minus(getApiJars()) - : getProject().getObjects().fileCollection(); - } - return toMinimize; - } - - - @Classpath - FileCollection getApiJars() { - if (apiJars == null) { - apiJars = minimizeJar - ? UnusedTracker.getApiJarsFromProject(getProject()) - : getProject().getObjects().fileCollection(); - } - return apiJars; - } - - - @InputFiles - @PathSensitive(PathSensitivity.RELATIVE) - FileCollection getSourceSetsClassesDirs() { - if (sourceSetsClassesDirs == null) { - ConfigurableFileCollection allClassesDirs = getProject().getObjects().fileCollection(); - if (minimizeJar) { - for (SourceSet sourceSet : getProject().getExtensions().getByType(SourceSetContainer.class)) { - FileCollection classesDirs = sourceSet.getOutput().getClassesDirs(); - allClassesDirs.from(classesDirs); - } - } - sourceSetsClassesDirs = allClassesDirs.filter(File::isDirectory); - } - return sourceSetsClassesDirs; - } - - @Internal - protected ZipCompressor getInternalCompressor() { - return GradleVersionUtil.getInternalCompressor(getEntryCompression(), this); - } - - @TaskAction - @Override - protected void copy() { - if (enableRelocation) { - RelocationUtil.configureRelocation(this, relocationPrefix); - } - from(getIncludedDependencies()); - super.copy(); - getLogger().info(shadowStats.toString()); - } - - @Classpath - public FileCollection getIncludedDependencies() { - return includedDependencies; - } - - /** - * Utility method for assisting between changes in Gradle 1.12 and 2.x. - * - * @return this - */ - @Internal - protected PatternSet getRootPatternSet() { - return GradleVersionUtil.getRootPatternSet(getMainSpec()); - } - - /** - * Configure inclusion/exclusion of module and project dependencies into uber jar. - * - * @param c the configuration of the filter - * @return this - */ - @Override - public ShadowJar dependencies(Action c) { - if (c != null) { - c.execute(dependencyFilter); - } - return this; - } - - /** - * Add a Transformer instance for modifying JAR resources and configure. - * - * @param clazz the transformer to add. Must have a no-arg constructor - * @return this - */ - @Override - public ShadowJar transform(Class clazz) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { - return transform(clazz, null); - } - - /** - * Add a Transformer instance for modifying JAR resources and configure. - * - * @param clazz the transformer class to add. Must have no-arg constructor - * @param c the configuration for the transformer - * @return this - */ - @Override - public ShadowJar transform(Class clazz, Action c) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { - T transformer = clazz.getDeclaredConstructor().newInstance(); - addTransform(transformer, c); - return this; - } - - private boolean isCacheableTransform(Class clazz) { - return clazz.isAnnotationPresent(CacheableTransformer.class); - } - - /** - * Add a preconfigured transformer instance. - * - * @param transformer the transformer instance to add - * @return this - */ - @Override - public ShadowJar transform(Transformer transformer) { - addTransform(transformer, null); - return this; - } - - private void addTransform(T transformer, Action c) { - if (c != null) { - c.execute(transformer); - } - - transformers.add(transformer); - } - - /** - * Syntactic sugar for merging service files in JARs. - * - * @return this - */ - @Override - public ShadowJar mergeServiceFiles() { - try { - transform(ServiceFileTransformer.class); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | - InstantiationException ignored) { - } - return this; - } - - /** - * Syntactic sugar for merging service files in JARs. - * - * @return this - */ - @Override - public ShadowJar mergeServiceFiles(final String rootPath) { - try { - transform(ServiceFileTransformer.class, serviceFileTransformer -> serviceFileTransformer.setPath(rootPath)); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | - InstantiationException ignored) { - } - return this; - } - - /** - * Syntactic sugar for merging service files in JARs. - * - * @return this - */ - @Override - public ShadowJar mergeServiceFiles(Action configureClosure) { - try { - transform(ServiceFileTransformer.class, configureClosure); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | - InstantiationException ignored) { - } - return this; - } - - /** - * Syntactic sugar for merging Groovy extension module descriptor files in JARs - * - * @return this - */ - @Override - public ShadowJar mergeGroovyExtensionModules() { - try { - transform(GroovyExtensionModuleTransformer.class); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | - InstantiationException ignored) { - } - return this; - } - - /** - * Syntax sugar for merging service files in JARs - * - * @return this - */ - @Override - public ShadowJar append(final String resourcePath) { - try { - transform(AppendingTransformer.class, transformer -> transformer.setResource(resourcePath)); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | - InstantiationException ignored) { - } - return this; - } - - /** - * Add a class relocator that maps each class in the pattern to the provided destination. - * - * @param pattern the source pattern to relocate - * @param destination the destination package - * @return this - */ - @Override - public ShadowJar relocate(String pattern, String destination) { - return relocate(pattern, destination, null); - } - - /** - * Add a class relocator that maps each class in the pattern to the provided destination. - * - * @param pattern the source pattern to relocate - * @param destination the destination package - * @param configure the configuration of the relocator - * @return this - */ - @Override - public ShadowJar relocate(String pattern, String destination, Action configure) { - SimpleRelocator relocator = new SimpleRelocator(pattern, destination, new ArrayList<>(), new ArrayList<>()); - addRelocator(relocator, configure); - return this; - } - - /** - * Add a relocator instance. - * - * @param relocator the relocator instance to add - * @return this - */ - @Override - public ShadowJar relocate(Relocator relocator) { - addRelocator(relocator, null); - return this; - } - - /** - * Add a relocator of the provided class. - * - * @param relocatorClass the relocator class to add. Must have a no-arg constructor. - * @return this - */ - @Override - public ShadowJar relocate(Class relocatorClass) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { - return relocate(relocatorClass, null); - } - - private void addRelocator(R relocator, Action configure) { - if (configure != null) { - configure.execute(relocator); - } - - relocators.add(relocator); - } - - /** - * Add a relocator of the provided class and configure. - * - * @param relocatorClass the relocator class to add. Must have a no-arg constructor - * @param configure the configuration for the relocator - * @return this - */ - @Override - public ShadowJar relocate(Class relocatorClass, Action configure) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { - R relocator = relocatorClass.getDeclaredConstructor().newInstance(); - addRelocator(relocator, configure); - return this; - } - - private boolean isCacheableRelocator(Class relocatorClass) { - return relocatorClass.isAnnotationPresent(CacheableRelocator.class); - } - - @Nested - public List getTransformers() { - return this.transformers; - } - - public void setTransformers(List transformers) { - this.transformers = transformers; - } - - @Nested - public List getRelocators() { - return this.relocators; - } - - public void setRelocators(List relocators) { - this.relocators = relocators; - } - - @Classpath @Optional - public List getConfigurations() { - return this.configurations; - } - - public void setConfigurations(List configurations) { - this.configurations = configurations; - } - - @Internal - public DependencyFilter getDependencyFilter() { - return this.dependencyFilter; - } - - public void setDependencyFilter(DependencyFilter filter) { - this.dependencyFilter = filter; - } - - @Input - public boolean isEnableRelocation() { - return enableRelocation; - } - - public void setEnableRelocation(boolean enableRelocation) { - this.enableRelocation = enableRelocation; - } - - @Input - public String getRelocationPrefix() { - return relocationPrefix; - } - - public void setRelocationPrefix(String relocationPrefix) { - this.relocationPrefix = relocationPrefix; - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.java b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.java deleted file mode 100644 index cc86e4e5c..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks; - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats; -import com.github.jengelman.gradle.plugins.shadow.internal.DependencyFilter; -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator; -import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator; -import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer; -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer; -import org.gradle.api.Action; -import org.gradle.api.file.CopySpec; - -import java.lang.reflect.InvocationTargetException; - -interface ShadowSpec extends CopySpec { - ShadowSpec minimize(); - - ShadowSpec minimize(Action configureClosure); - - ShadowSpec dependencies(Action configure); - - ShadowSpec transform(Class clazz) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException; - - ShadowSpec transform(Class clazz, Action configure) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException; - - ShadowSpec transform(Transformer transformer); - - ShadowSpec mergeServiceFiles(); - - ShadowSpec mergeServiceFiles(String rootPath); - - ShadowSpec mergeServiceFiles(Action configureClosure); - - ShadowSpec mergeGroovyExtensionModules(); - - ShadowSpec append(String resourcePath); - - ShadowSpec relocate(String pattern, String destination); - - ShadowSpec relocate(String pattern, String destination, Action configure); - - ShadowSpec relocate(Relocator relocator); - - ShadowSpec relocate(Class clazz) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException; - - ShadowSpec relocate(Class clazz, Action configure) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException; - - ShadowStats getStats(); -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.groovy deleted file mode 100644 index 569066ae1..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.groovy +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement - -/** - * Prevents duplicate copies of the license - *

- * Modified from org.apache.maven.plugins.shade.resouce.ApacheLicenseResourceTransformer.java - * - * @author John Engelman - */ -class ApacheLicenseResourceTransformer implements Transformer { - - private static final String LICENSE_PATH = "META-INF/LICENSE" - - private static final String LICENSE_TXT_PATH = "META-INF/LICENSE.txt" - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - return LICENSE_PATH.equalsIgnoreCase(path) || - LICENSE_TXT_PATH.regionMatches(true, 0, path, 0, LICENSE_TXT_PATH.length()) - } - - @Override - void transform(TransformerContext context) { - - } - - @Override - boolean hasTransformedResource() { - return false - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.groovy deleted file mode 100644 index 77ef87fbc..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.groovy +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.StringUtils -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional - -import java.text.SimpleDateFormat - -/** - * Merges META-INF/NOTICE.TXT files. - *

- * Modified from org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer.java - * - * @author John Engelman - */ -class ApacheNoticeResourceTransformer implements Transformer { - - private Set entries = new LinkedHashSet() - - private Map> organizationEntries = new LinkedHashMap>() - - @Input - String projectName = "" // MSHADE-101 :: NullPointerException when projectName is missing - - @Input - boolean addHeader = true - - @Input - String preamble1 = "// ------------------------------------------------------------------\n" + - "// NOTICE file corresponding to the section 4d of The Apache License,\n" + - "// Version 2.0, in this case for " - - @Input - String preamble2 = "\n// ------------------------------------------------------------------\n" - - @Input - String preamble3 = "This product includes software developed at\n" - - @Input - String organizationName = "The Apache Software Foundation" - - @Input - String organizationURL = "http://www.apache.org/" - - @Input - String inceptionYear = "2006" - - @Optional - @Input - String copyright - - /** - * The file encoding of the NOTICE file. - */ - @Optional - @Input - String encoding - - private static final String NOTICE_PATH = "META-INF/NOTICE" - - private static final String NOTICE_TXT_PATH = "META-INF/NOTICE.txt" - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (NOTICE_PATH.equalsIgnoreCase(path) || NOTICE_TXT_PATH.equalsIgnoreCase(path)) { - return true - } - - return false - } - - @Override - void transform(TransformerContext context) { - if (entries.isEmpty()) { - String year = new SimpleDateFormat("yyyy").format(new Date()) - if (inceptionYear != year) { - year = inceptionYear + "-" + year - } - - //add headers - if (addHeader) { - entries.add(preamble1 + projectName + preamble2) - } else { - entries.add("") - } - //fake second entry, we'll look for a real one later - entries.add(projectName + "\nCopyright " + year + " " + organizationName + "\n") - entries.add(preamble3 + organizationName + " (" + organizationURL + ").\n") - } - - BufferedReader reader - if (StringUtils.isNotEmpty(encoding)) { - reader = new BufferedReader(new InputStreamReader(context.is, encoding)) - } else { - reader = new BufferedReader(new InputStreamReader(context.is)) - } - - String line = reader.readLine() - StringBuffer sb = new StringBuffer() - Set currentOrg = null - int lineCount = 0 - while (line != null) { - String trimedLine = line.trim() - - if (!trimedLine.startsWith("//")) { - if (trimedLine.length() > 0) { - if (trimedLine.startsWith("- ")) { - //resource-bundle 1.3 mode - if (lineCount == 1 - && sb.toString().indexOf("This product includes/uses software(s) developed by") != -1) { - currentOrg = organizationEntries.get(sb.toString().trim()) - if (currentOrg == null) { - currentOrg = new TreeSet() - organizationEntries.put(sb.toString().trim(), currentOrg) - } - sb = new StringBuffer() - } else if (sb.length() > 0 && currentOrg != null) { - currentOrg.add(sb.toString()) - sb = new StringBuffer() - } - - } - sb.append(line).append("\n") - lineCount++ - } else { - String ent = sb.toString() - if (ent.startsWith(projectName) && ent.indexOf("Copyright ") != -1) { - copyright = ent - } - if (currentOrg == null) { - entries.add(ent) - } else { - currentOrg.add(ent) - } - sb = new StringBuffer() - lineCount = 0 - currentOrg = null - } - } - - line = reader.readLine() - } - if (sb.length() > 0) { - if (currentOrg == null) { - entries.add(sb.toString()) - } else { - currentOrg.add(sb.toString()) - } - } - } - - @Override - boolean hasTransformedResource() { - return true - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - ZipEntry zipEntry = new ZipEntry(NOTICE_PATH) - zipEntry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, zipEntry.time) - os.putNextEntry(zipEntry) - - Writer pow - if (StringUtils.isNotEmpty(encoding)) { - pow = new OutputStreamWriter(os, encoding) - } else { - pow = new OutputStreamWriter(os) - } - PrintWriter writer = new PrintWriter(pow) - - int count = 0 - for (String line : entries) { - ++count - if (line == copyright && count != 2) { - continue - } - - if (count == 2 && copyright != null) { - writer.print(copyright) - writer.print('\n') - } else { - writer.print(line) - writer.print('\n') - } - if (count == 3) { - //do org stuff - for (Map.Entry> entry : organizationEntries.entrySet()) { - writer.print(entry.getKey()) - writer.print('\n') - for (String l : entry.getValue()) { - writer.print(l) - } - writer.print('\n') - } - } - } - - writer.flush() - - entries.clear() - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.groovy deleted file mode 100644 index 7deb8ea50..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.groovy +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.IOUtil -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional - -/** - * A resource processor that appends content for a resource, separated by a newline. - * - * Modified from org.apache.maven.plugins.shade.resource.AppendingTransformer.java - * - * Modifications - * @author John Engelman - */ -@CacheableTransformer -class AppendingTransformer implements Transformer { - - @Optional - @Input - String resource - - /** - * Defer initialization, see https://github.com/GradleUp/shadow/issues/763 - */ - private ByteArrayOutputStream data - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (resource != null && resource.equalsIgnoreCase(path)) { - return true - } - - return false - } - - @Override - void transform(TransformerContext context) { - if (data == null) { - data = new ByteArrayOutputStream() - } - - IOUtil.copy(context.is, data) - data.write('\n'.bytes) - - context.is.close() - } - - @Override - boolean hasTransformedResource() { - return data?.size() > 0 - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - if (data == null) { - data = new ByteArrayOutputStream() - } - - ZipEntry entry = new ZipEntry(resource) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - - IOUtil.copy(new ByteArrayInputStream(data.toByteArray()), os) - data.reset() - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.groovy deleted file mode 100644 index ae3b74e56..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.groovy +++ /dev/null @@ -1,17 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import java.lang.annotation.ElementType -import java.lang.annotation.Retention -import java.lang.annotation.RetentionPolicy -import java.lang.annotation.Target - -/** - * Marks that a given instance of {@link Transformer} is is compatible with the Gradle build cache. - * In other words, it has its appropriate inputs annotated so that Gradle can consider them when - * determining the cache key. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@interface CacheableTransformer { - -} \ No newline at end of file diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.groovy deleted file mode 100644 index 58483d3cf..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.groovy +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.IOUtil -import org.codehaus.plexus.util.xml.XmlStreamReader -import org.codehaus.plexus.util.xml.XmlStreamWriter -import org.codehaus.plexus.util.xml.Xpp3Dom -import org.codehaus.plexus.util.xml.Xpp3DomBuilder -import org.codehaus.plexus.util.xml.Xpp3DomWriter -import org.gradle.api.file.FileTreeElement - -/** - * A resource processor that aggregates plexus components.xml files. - *

- * Modified from org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer.java - * - * @author John Engelman - */ -class ComponentsXmlResourceTransformer implements Transformer { - private Map components = new LinkedHashMap() - - public static final String COMPONENTS_XML_PATH = "META-INF/plexus/components.xml" - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - return COMPONENTS_XML_PATH == path - } - - @Override - void transform(TransformerContext context) { - Xpp3Dom newDom - - try { - BufferedInputStream bis = new BufferedInputStream(context.is) { - void close() - throws IOException { - // leave ZIP open - } - } - - Reader reader = new XmlStreamReader(bis) - - newDom = Xpp3DomBuilder.build(reader) - } - catch (Exception e) { - throw (IOException) new IOException("Error parsing components.xml in " + context.is).initCause(e) - } - - // Only try to merge in components if there are some elements in the component-set - if (newDom.getChild("components") == null) { - return - } - - Xpp3Dom[] children = newDom.getChild("components").getChildren("component") - - for (int i = 0; i < children.length; i++) { - Xpp3Dom component = children[i] - - String role = getValue(component, "role") - role = getRelocatedClass(role, context) - setValue(component, "role", role) - - String roleHint = getValue(component, "role-hint") - - String impl = getValue(component, "implementation") - impl = getRelocatedClass(impl, context) - setValue(component, "implementation", impl) - - String key = role + ':' + roleHint - if (components.containsKey(key)) { - // TODO: use the tools in Plexus to merge these properly. For now, I just need an all-or-nothing - // configuration carry over - - Xpp3Dom dom = components.get(key) - if (dom.getChild("configuration") != null) { - component.addChild(dom.getChild("configuration")) - } - } - - Xpp3Dom requirements = component.getChild("requirements") - if (requirements != null && requirements.getChildCount() > 0) { - for (int r = requirements.getChildCount() - 1; r >= 0; r--) { - Xpp3Dom requirement = requirements.getChild(r) - - String requiredRole = getValue(requirement, "role") - requiredRole = getRelocatedClass(requiredRole, context) - setValue(requirement, "role", requiredRole) - } - } - - components.put(key, component) - } - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - byte[] data = getTransformedResource() - - ZipEntry entry = new ZipEntry(COMPONENTS_XML_PATH) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - - os.putNextEntry(entry) - - IOUtil.copy(data, os) - - components.clear() - } - - @Override - boolean hasTransformedResource() { - return !components.isEmpty() - } - - private byte[] getTransformedResource() - throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(1024 * 4) - - try (Writer writer = new XmlStreamWriter(baos)) { - Xpp3Dom dom = new Xpp3Dom("component-set") - - Xpp3Dom componentDom = new Xpp3Dom("components") - - dom.addChild(componentDom) - - for (Xpp3Dom component : components.values()) { - componentDom.addChild(component) - } - - Xpp3DomWriter.write(writer, dom) - } - - return baos.toByteArray() - } - - private static String getRelocatedClass(String className, TransformerContext context) { - List relocators = context.relocators - ShadowStats stats = context.stats - if (className != null && className.length() > 0 && relocators != null) { - for (Relocator relocator : relocators) { - if (relocator.canRelocateClass(className)) { - RelocateClassContext relocateClassContext = new RelocateClassContext(className, stats) - return relocator.relocateClass(relocateClassContext) - } - } - } - - return className - } - - private static String getValue(Xpp3Dom dom, String element) { - Xpp3Dom child = dom.getChild(element) - - return (child != null && child.getValue() != null) ? child.getValue() : "" - } - - private static void setValue(Xpp3Dom dom, String element, String value) { - Xpp3Dom child = dom.getChild(element) - - if (child == null || value == null || value.length() <= 0) { - return - } - - child.setValue(value) - } - -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.groovy deleted file mode 100644 index 12eb55e10..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.groovy +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.StringUtils -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional - -/** - * A resource processor that prevents the inclusion of an arbitrary - * resource into the shaded JAR. - *

- * Modified from org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer.java - * - * @author John Engelman - */ -class DontIncludeResourceTransformer implements Transformer { - - @Optional - @Input - String resource - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (StringUtils.isNotEmpty(resource) && path.endsWith(resource)) { - return true - } - - return false - } - - @Override - void transform(TransformerContext context) { - // no op - } - - @Override - boolean hasTransformedResource() { - return false - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - // no op - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.groovy deleted file mode 100644 index 62255f3df..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.groovy +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.IOUtil -import org.gradle.api.file.FileTreeElement - -/** - * Modified from eu.appsatori.gradle.fatjar.tasks.PrepareFiles.groovy - *

- * Resource transformer that merges Groovy extension module descriptor files into a single file. - * Groovy extension module descriptor files have the name org.codehaus.groovy.runtime.ExtensionModule - * and live in the META-INF/services (Groovy up to 2.4) or META-INF/groovy (Groovy 2.5+) directory. - * See https://issues.apache.org/jira/browse/GROOVY-8480 for more details of the change. - * - * If there are several descriptor files spread across many JARs the individual - * entries will be merged into a single descriptor file which will be - * packaged into the resultant JAR produced by the shadowing process. - * It will live in the legacy directory (META-INF/services) if all of the processed descriptor - * files came from the legacy location, otherwise it will be written into the now standard location (META-INF/groovy). - * Note that certain JDK9+ tooling will break when using the legacy location. - */ -@CacheableTransformer -class GroovyExtensionModuleTransformer implements Transformer { - - private static final GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH = - "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" - private static final GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH = - "META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule" - - private static final MODULE_NAME_KEY = 'moduleName' - private static final MODULE_VERSION_KEY = 'moduleVersion' - private static final EXTENSION_CLASSES_KEY = 'extensionClasses' - private static final STATIC_EXTENSION_CLASSES_KEY = 'staticExtensionClasses' - - private static final MERGED_MODULE_NAME = 'MergedByShadowJar' - private static final MERGED_MODULE_VERSION = '1.0.0' - - private final Properties module = new Properties() - private boolean legacy = true // default to Groovy 2.4 or earlier - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (path == GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH) { - legacy = false // Groovy 2.5+ - return true - } - return path == GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH - } - - @Override - void transform(TransformerContext context) { - def props = new Properties() - props.load(context.is) - props.each { String key, String value -> - switch (key) { - case MODULE_NAME_KEY: - handle(key, value) { - module.setProperty(key, MERGED_MODULE_NAME) - } - break - case MODULE_VERSION_KEY: - handle(key, value) { - module.setProperty(key, MERGED_MODULE_VERSION) - } - break - case [EXTENSION_CLASSES_KEY, STATIC_EXTENSION_CLASSES_KEY]: - handle(key, value) { String existingValue -> - def newValue = "${existingValue},${value}" - module.setProperty(key, newValue) - } - break - } - } - } - - private handle(String key, String value, Closure mergeValue) { - def existingValue = module.getProperty(key) - if (existingValue) { - mergeValue(existingValue) - } else { - module.setProperty(key, value) - } - } - - @Override - boolean hasTransformedResource() { - return module.size() > 0 - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - ZipEntry entry = new ZipEntry(legacy ? GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH : GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - IOUtil.copy(toInputStream(module), os) - os.closeEntry() - } - - private static InputStream toInputStream(Properties props) { - def baos = new ByteArrayOutputStream() - props.store(baos, null) - return new ByteArrayInputStream(baos.toByteArray()) - } - -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.groovy deleted file mode 100644 index 3a829e873..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.groovy +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.IOUtil -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.PathSensitive -import org.gradle.api.tasks.PathSensitivity - -/** - * A resource processor that allows the addition of an arbitrary file - * content into the shaded JAR. - *

- * Modified from org.apache.maven.plugins.shade.resource.IncludeResourceTransformer.java - * - * @author John Engelman - */ -class IncludeResourceTransformer implements Transformer { - - @InputFile - @PathSensitive(PathSensitivity.NONE) - File file - - @Input - String resource - - @Override - boolean canTransformResource(FileTreeElement element) { - return false - } - - @Override - void transform(TransformerContext context) { - // no op - } - - @Override - boolean hasTransformedResource() { - return file != null ? file.exists() : false - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - ZipEntry entry = new ZipEntry(resource) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - - InputStream is = new FileInputStream(file) - IOUtil.copy(is, os) - is.close() - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.groovy deleted file mode 100644 index 7b2d831f8..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.groovy +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import org.apache.commons.io.IOUtils -import org.apache.commons.io.output.CloseShieldOutputStream -import org.apache.logging.log4j.core.config.plugins.processor.PluginCache -import org.apache.logging.log4j.core.config.plugins.processor.PluginEntry -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement - -import static org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor.PLUGIN_CACHE_FILE - -/** - * Modified from the maven equivalent to work with gradle - * - * @author Paul Nelson Baker - * @see LinkedIn - * @see GitHub - * @see edwgiz/maven-shaded-log4j-transformer - * @see PluginsCacheFileTransformer.java - */ -@CacheableTransformer -class Log4j2PluginsCacheFileTransformer implements Transformer { - - private final List temporaryFiles - private final List relocators - - private ShadowStats stats - - Log4j2PluginsCacheFileTransformer() { - temporaryFiles = new ArrayList<>() - relocators = new ArrayList<>() - } - - @Override - boolean canTransformResource(FileTreeElement element) { - return PLUGIN_CACHE_FILE == element.name - } - - @Override - void transform(TransformerContext context) { - def inputStream = context.is - def temporaryFile = File.createTempFile("Log4j2Plugins", ".dat") - temporaryFile.deleteOnExit() - temporaryFiles.add(temporaryFile) - FileOutputStream fos = new FileOutputStream(temporaryFile) - try { - IOUtils.copy(inputStream, fos) - } finally { - fos.close() - } - def contextRelocators = context.relocators - if (contextRelocators != null) { - this.relocators.addAll(contextRelocators) - } - if (this.stats == null) { - this.stats = context.stats - } - } - - @Override - boolean hasTransformedResource() { - // This functionality matches the original plugin, however, I'm not clear what - // the exact logic is. From what I can tell temporaryFiles should be never be empty - // if anything has been performed. - def hasTransformedMultipleFiles = temporaryFiles.size() > 1 - def hasAtLeastOneFileAndRelocator = !temporaryFiles.isEmpty() && !relocators.isEmpty() - def hasTransformedResources = hasTransformedMultipleFiles || hasAtLeastOneFileAndRelocator - return hasTransformedResources - } - - @Override - void modifyOutputStream(ZipOutputStream zipOutputStream, boolean preserveFileTimestamps) { - PluginCache pluginCache = new PluginCache() - pluginCache.loadCacheFiles(getUrlEnumeration()) - relocatePlugins(pluginCache) - ZipEntry entry = new ZipEntry(PLUGIN_CACHE_FILE) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - zipOutputStream.putNextEntry(entry) - pluginCache.writeCache(CloseShieldOutputStream.wrap(zipOutputStream)) - temporaryFiles.clear() - } - - private Enumeration getUrlEnumeration() { - def urls = temporaryFiles.collect({ it.toURI().toURL() }).asList() - return Collections.enumeration(urls) - } - - private void relocatePlugins(PluginCache pluginCache) { - for (Map currentMap : pluginCache.getAllCategories().values()) { - pluginEntryLoop: - for (PluginEntry currentPluginEntry : currentMap.values()) { - String className = currentPluginEntry.getClassName() - RelocateClassContext relocateClassContext = new RelocateClassContext(className, stats) - for (Relocator currentRelocator : relocators) { - // If we have a relocator that can relocate our current entry... - if (currentRelocator.canRelocateClass(className)) { - // Then we perform that relocation and update the plugin entry to reflect the new value. - String relocatedClassName = currentRelocator.relocateClass(relocateClassContext) - currentPluginEntry.setClassName(relocatedClassName) - continue pluginEntryLoop - } - } - } - } - } -} \ No newline at end of file diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.groovy deleted file mode 100644 index bff6601d5..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.groovy +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import groovy.util.logging.Slf4j -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.IOUtil -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input - -import static java.nio.charset.StandardCharsets.UTF_8 -import static java.util.jar.JarFile.MANIFEST_NAME - -/** - * A resource processor that can append arbitrary attributes to the first MANIFEST.MF - * that is found in the set of JARs being processed. The attributes are appended in - * the specified order, and duplicates are allowed. - *

- * Modified from {@link ManifestResourceTransformer}. - * @author Chris Rankin - */ -@Slf4j -class ManifestAppenderTransformer implements Transformer { - private static final byte[] EOL = "\r\n".getBytes(UTF_8) - private static final byte[] SEPARATOR = ": ".getBytes(UTF_8) - - private byte[] manifestContents = [] - private final List>> attributes = [] - - @Input - List>> getAttributes() { attributes } - - ManifestAppenderTransformer append(String name, Comparable value) { - attributes.add(new Tuple2>(name, value)) - this - } - - @Override - boolean canTransformResource(FileTreeElement element) { - MANIFEST_NAME.equalsIgnoreCase(element.relativePath.pathString) - } - - @Override - void transform(TransformerContext context) { - if (manifestContents.length == 0) { - manifestContents = IOUtil.toByteArray(context.is) - try { - context.is - } catch (IOException e) { - log.warn("Failed to read MANIFEST.MF", e) - } - } - } - - @Override - boolean hasTransformedResource() { - !attributes.isEmpty() - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - ZipEntry entry = new ZipEntry(MANIFEST_NAME) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - os.write(manifestContents) - - if (!attributes.isEmpty()) { - for (attribute in attributes) { - os.write(attribute.v1.getBytes(UTF_8)) - os.write(SEPARATOR) - os.write(attribute.v2.toString().getBytes(UTF_8)) - os.write(EOL) - } - os.write(EOL) - attributes.clear() - } - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.groovy deleted file mode 100644 index cbc225a55..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.groovy +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import groovy.util.logging.Slf4j -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional - -import java.util.jar.Attributes -import java.util.jar.Attributes.Name -import java.util.jar.JarFile -import java.util.jar.Manifest - -/** - * A resource processor that allows the arbitrary addition of attributes to - * the first MANIFEST.MF that is found in the set of JARs being processed, or - * to a newly created manifest for the shaded JAR. - *

- * Modified from org.apache.maven.plugins.shade.resource.ManifestResourceTransformer - * @author Jason van Zyl - * @author John Engelman - */ -@Slf4j -class ManifestResourceTransformer implements Transformer { - - // Configuration - @Optional - @Input - String mainClass - - @Optional - @Input - Map manifestEntries - - // Fields - private boolean manifestDiscovered - - private Manifest manifest - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (JarFile.MANIFEST_NAME.equalsIgnoreCase(path)) { - return true - } - - return false - } - - @Override - void transform(TransformerContext context) { - // We just want to take the first manifest we come across as that's our project's manifest. This is the behavior - // now which is situational at best. Right now there is no context passed in with the processing so we cannot - // tell what artifact is being processed. - if (!manifestDiscovered) { - manifest = new Manifest(context.is) - manifestDiscovered = true - try { - context.is - } catch (IOException e) { - log.warn("Failed to read MANIFEST.MF", e) - } - } - } - - @Override - boolean hasTransformedResource() { - return true - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - // If we didn't find a manifest, then let's create one. - if (manifest == null) { - manifest = new Manifest() - } - - Attributes attributes = manifest.getMainAttributes() - - if (mainClass != null) { - attributes.put(Name.MAIN_CLASS, mainClass) - } - - if (manifestEntries != null) { - for (Map.Entry entry : manifestEntries.entrySet()) { - attributes.put(new Name(entry.getKey()), entry.getValue()) - } - } - - ZipEntry entry = new ZipEntry(JarFile.MANIFEST_NAME) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - manifest.write(os) - } - - ManifestResourceTransformer attributes(Map attributes) { - if (manifestEntries == null) { - manifestEntries = [:] - } - manifestEntries.putAll(attributes) - this - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.groovy deleted file mode 100644 index 044e3dbaa..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.groovy +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.internal.CleanProperties -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.IOUtil -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal - -import static groovy.lang.Closure.IDENTITY - -/** - * Resources transformer that merges Properties files. - * - *

The default merge strategy discards duplicate values coming from additional - * resources. This behavior can be changed by setting a value for the mergeStrategy - * property, such as 'first' (default), 'latest' or 'append'. If the merge strategy is - * 'latest' then the last value of a matching property entry will be used. If the - * merge strategy is 'append' then the property values will be combined, using a - * merge separator (default value is ','). The merge separator can be changed by - * setting a value for the mergeSeparator property.

- * - * Say there are two properties files A and B with the - * following entries: - * - * A - *
    - *
  • key1 = value1
  • - *
  • key2 = value2
  • - *
- * - * B - *
    - *
  • key2 = balue2
  • - *
  • key3 = value3
  • - *
- * - * With mergeStrategy = first you get - * - * C - *
    - *
  • key1 = value1
  • - *
  • key2 = value2
  • - *
  • key3 = value3
  • - *
- * - * With mergeStrategy = latest you get - * - * C - *
    - *
  • key1 = value1
  • - *
  • key2 = balue2
  • - *
  • key3 = value3
  • - *
- * - * With mergeStrategy = append and mergeSparator = ; you get - * - * C - *
    - *
  • key1 = value1
  • - *
  • key2 = value2;balue2
  • - *
  • key3 = value3
  • - *
- * - *

There are three additional properties that can be set: paths, mappings, - * and keyTransformer. - * The first contains a list of strings or regexes that will be used to determine if - * a path should be transformed or not. The merge strategy and merge separator are - * taken from the global settings.

- * - *

The mappings property allows you to define merge strategy and separator per - * path

. If either paths or mappings is defined then no other path - * entries will be merged. mappings has precedence over paths if both - * are defined.

- * - *

If you need to transform keys in properties files, e.g. because they contain class - * names about to be relocated, you can set the keyTransformer property to a - * closure that receives the original key and returns the key name to be used.

- * - *

Example:

- *
- * import org.codehaus.griffon.gradle.shadow.transformers.*
- * tasks.named('shadowJar', ShadowJar) {
- *     transform(PropertiesFileTransformer) {
- *         paths = [
- *             'META-INF/editors/java.beans.PropertyEditor'
- *         ]
- *         keyTransformer = { key ->
- *             key.replaceAll('^(orig\.package\..*)$', 'new.prefix.$1')
- *         }
- *     }
- * }
- * 
- * - * @author Andres Almiray - * @author Marc Philipp - */ -class PropertiesFileTransformer implements Transformer { - private static final String PROPERTIES_SUFFIX = '.properties' - - private Map propertiesEntries = [:] - - @Input - List paths = [] - - @Input - Map> mappings = [:] - - @Input - String mergeStrategy = 'first' // latest, append - - @Input - String mergeSeparator = ',' - - @Input - String charset = 'ISO_8859_1' - - @Internal - Closure keyTransformer = IDENTITY - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (mappings.containsKey(path)) return true - for (key in mappings.keySet()) { - if (path =~ /$key/) return true - } - - if (path in paths) return true - for (p in paths) { - if (path =~ /$p/) return true - } - - !mappings && !paths && path.endsWith(PROPERTIES_SUFFIX) - } - - @Override - void transform(TransformerContext context) { - Properties props = propertiesEntries[context.path] - Properties incoming = loadAndTransformKeys(context.is) - if (props == null) { - propertiesEntries[context.path] = incoming - } else { - incoming.each { key, value -> - if (props.containsKey(key)) { - switch (mergeStrategyFor(context.path).toLowerCase()) { - case 'latest': - props.put(key, value) - break - case 'append': - props.put(key, props.getProperty(key) + mergeSeparatorFor(context.path) + value) - break - case 'first': - default: - // continue - break - } - } else { - props.put(key, value) - } - } - } - } - - private Properties loadAndTransformKeys(InputStream is) { - Properties props = new CleanProperties() - // InputStream closed by caller, so we don't do it here. - if (is != null) { - props.load(new InputStreamReader(is, charset)) - } - return transformKeys(props) - } - - private Properties transformKeys(Properties properties) { - if (keyTransformer == IDENTITY) { - return properties - } - def result = new CleanProperties() - properties.each { key, value -> - result.put(keyTransformer.call(key), value) - } - return result - } - - private String mergeStrategyFor(String path) { - if (mappings.containsKey(path)) { - return mappings.get(path).mergeStrategy ?: mergeStrategy - } - for (key in mappings.keySet()) { - if (path =~ /$key/) { - return mappings.get(key).mergeStrategy ?: mergeStrategy - } - } - - return mergeStrategy - } - - private String mergeSeparatorFor(String path) { - if (mappings.containsKey(path)) { - return mappings.get(path).mergeSeparator ?: mergeSeparator - } - for (key in mappings.keySet()) { - if (path =~ /$key/) { - return mappings.get(key).mergeSeparator ?: mergeSeparator - } - } - - return mergeSeparator - } - - @Override - boolean hasTransformedResource() { - propertiesEntries.size() > 0 - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - // cannot close the writer as the OutputStream needs to remain open - def zipWriter = new OutputStreamWriter(os, charset) - propertiesEntries.each { String path, Properties props -> - ZipEntry entry = new ZipEntry(path) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - IOUtil.copy(readerFor(props, charset), zipWriter) - zipWriter.flush() - os.closeEntry() - } - } - - private static InputStreamReader readerFor(Properties props, String charset) { - ByteArrayOutputStream baos = new ByteArrayOutputStream() - baos.withWriter(charset) { w -> - props.store(w, '') - } - new InputStreamReader(new ByteArrayInputStream(baos.toByteArray()), charset) - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.groovy deleted file mode 100644 index b2153a3f9..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.groovy +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement -import org.gradle.api.specs.Spec -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.util.PatternFilterable -import org.gradle.api.tasks.util.PatternSet -import org.codehaus.plexus.util.IOUtil - -/** - * Modified from org.apache.maven.plugins.shade.resource.ServiceResourceTransformer.java - *

- * Resources transformer that appends entries in META-INF/services resources into - * a single resource. For example, if there are several META-INF/services/org.apache.maven.project.ProjectBuilder - * resources spread across many JARs the individual entries will all be concatenated into a single - * META-INF/services/org.apache.maven.project.ProjectBuilder resource packaged into the resultant JAR produced - * by the shading process. - * - * @author jvanzyl - * @author Charlie Knudsen - * @author John Engelman - */ -@CacheableTransformer -class ServiceFileTransformer implements Transformer, PatternFilterable { - - private static final String SERVICES_PATTERN = "META-INF/services/**" - - private static final String GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN = - "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" - - private Map serviceEntries = [:].withDefault { new ServiceStream() } - - private final PatternSet patternSet = - new PatternSet().include(SERVICES_PATTERN).exclude(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN) - - void setPath(String path) { - patternSet.setIncludes(["${path}/**"]) - } - - @Override - boolean canTransformResource(FileTreeElement element) { - FileTreeElement target = element instanceof ShadowCopyAction.ArchiveFileTreeElement ? element.asFileTreeElement() : element - return patternSet.asSpec.isSatisfiedBy(target) - } - - @Override - void transform(TransformerContext context) { - def lines = context.is.readLines() - def targetPath = context.path - context.relocators.each {rel -> - if (rel.canRelocateClass(new File(targetPath).name)) { - targetPath = rel.relocateClass(RelocateClassContext.builder().className(targetPath).stats(context.stats).build()) - } - lines.eachWithIndex { String line, int i -> - if (rel.canRelocateClass(line)) { - def lineContext = RelocateClassContext.builder().className(line).stats(context.stats).build() - lines[i] = rel.relocateClass(lineContext) - } - } - } - lines.each {line -> serviceEntries[targetPath].append(new ByteArrayInputStream(line.getBytes()))} - } - - @Override - boolean hasTransformedResource() { - return serviceEntries.size() > 0 - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - serviceEntries.each { String path, ServiceStream stream -> - ZipEntry entry = new ZipEntry(path) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - IOUtil.copy(stream.toInputStream(), os) - os.closeEntry() - } - } - - static class ServiceStream extends ByteArrayOutputStream { - - ServiceStream(){ - super( 1024 ) - } - - void append(InputStream is ) throws IOException { - if ( super.count > 0 && super.buf[super.count - 1] != '\n' && super.buf[super.count - 1] != '\r' ) { - byte[] newline = '\n'.bytes - write(newline, 0, newline.length) - } - IOUtil.copy(is, this) - } - - InputStream toInputStream() { - return new ByteArrayInputStream( super.buf, 0, super.count ) - } - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer include(String... includes) { - patternSet.include(includes) - return this - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer include(Iterable includes) { - patternSet.include(includes) - return this - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer include(Spec includeSpec) { - patternSet.include(includeSpec) - return this - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer include(Closure includeSpec) { - patternSet.include(includeSpec) - return this - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer exclude(String... excludes) { - patternSet.exclude(excludes) - return this - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer exclude(Iterable excludes) { - patternSet.exclude(excludes) - return this - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer exclude(Spec excludeSpec) { - patternSet.exclude(excludeSpec) - return this - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer exclude(Closure excludeSpec) { - patternSet.exclude(excludeSpec) - return this - } - - /** - * {@inheritDoc} - */ - @Override - @Input - Set getIncludes() { - return patternSet.includes - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer setIncludes(Iterable includes) { - patternSet.includes = includes - return this - } - - /** - * {@inheritDoc} - */ - @Override - @Input - Set getExcludes() { - return patternSet.excludes - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer setExcludes(Iterable excludes) { - patternSet.excludes = excludes - return this - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.groovy deleted file mode 100644 index 630d41301..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.groovy +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.Named -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Internal - -/** - * Modified from org.apache.maven.plugins.shade.resource.ResourceTransformer.java - * - * @author Jason van Zyl - * @author Charlie Knudsen - * @author John Engelman - */ -trait Transformer implements Named { - - abstract boolean canTransformResource(FileTreeElement element) - - abstract void transform(TransformerContext context) - - abstract boolean hasTransformedResource() - - abstract void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) - - @Internal - String getName() { - return getClass().simpleName - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.groovy deleted file mode 100644 index b951dfa66..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.groovy +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction -import groovy.transform.Canonical -import groovy.transform.builder.Builder - - -@Canonical -@Builder -class TransformerContext { - - String path - InputStream is - List relocators - ShadowStats stats - - static long getEntryTimestamp(boolean preserveFileTimestamps, long entryTime) { - preserveFileTimestamps ? entryTime : ShadowCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.groovy deleted file mode 100644 index 76e12f06d..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.groovy +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional -import org.jdom2.Attribute -import org.jdom2.Content -import org.jdom2.Document -import org.jdom2.Element -import org.jdom2.JDOMException -import org.jdom2.input.SAXBuilder -import org.jdom2.input.sax.XMLReaders -import org.jdom2.output.Format -import org.jdom2.output.XMLOutputter -import org.xml.sax.EntityResolver -import org.xml.sax.InputSource -import org.xml.sax.SAXException - -/** - * Appends multiple occurrences of some XML file. - *

- * Modified from org.apache.maven.plugins.shade.resource.XmlAppendingTransformer.java - * - * @author John Engelman - */ -@CacheableTransformer -class XmlAppendingTransformer implements Transformer { - public static final String XSI_NS = "http://www.w3.org/2001/XMLSchema-instance" - - @Input - boolean ignoreDtd = true - - @Optional - @Input - String resource - - private Document doc - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (resource != null && resource.equalsIgnoreCase(path)) { - return true - } - - return false - } - - @Override - void transform(TransformerContext context) { - Document r - try { - SAXBuilder builder = new SAXBuilder(XMLReaders.NONVALIDATING) - builder.setExpandEntities(false) - if (ignoreDtd) { - builder.setEntityResolver(new EntityResolver() { - InputSource resolveEntity(String publicId, String systemId) - throws SAXException, IOException { - return new InputSource(new StringReader("")) - } - }) - } - r = builder.build(context.is) - } - catch (JDOMException e) { - throw new RuntimeException("Error processing resource " + resource + ": " + e.getMessage(), e) - } - - if (doc == null) { - doc = r - } else { - Element root = r.getRootElement() - - root.attributes.each { Attribute a -> - - Element mergedEl = doc.getRootElement() - Attribute mergedAtt = mergedEl.getAttribute(a.getName(), a.getNamespace()) - if (mergedAtt == null) { - mergedEl.setAttribute(a) - } - } - - root.children.each { Content n -> - doc.getRootElement().addContent(n.clone()) - } - } - } - - @Override - boolean hasTransformedResource() { - return doc != null - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - ZipEntry entry = new ZipEntry(resource) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - new XMLOutputter(Format.getPrettyFormat()).output(doc, os) - - doc = null - } -} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/DependencyFilter.kt new file mode 100644 index 000000000..95f54e6b5 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/DependencyFilter.kt @@ -0,0 +1,54 @@ +package com.github.jengelman.gradle.plugins.shadow + +import groovy.lang.Closure +import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.ResolvedDependency +import org.gradle.api.file.FileCollection +import org.gradle.api.specs.Spec + +public interface DependencyFilter { + /** + * Resolve a FileCollection against the include/exclude rules in the filter + */ + public fun resolve(configuration: FileCollection): FileCollection + + /** + * Resolve all FileCollections against the include/exclude ruels in the filter and combine the results + */ + public fun resolve(configurations: Collection): FileCollection + + /** + * Exclude dependencies that match the provided spec. + */ + public fun exclude(spec: Spec): DependencyFilter + + /** + * Include dependencies that match the provided spec. + */ + public fun include(spec: Spec): DependencyFilter + + /** + * Create a spec that matches the provided project notation on group, name, and version + */ + public fun project(notation: Map): Spec + + /** + * Create a spec that matches the default configuration for the provided project path on group, name, and version + */ + public fun project(notation: String): Spec + + /** + * Create a spec that matches dependencies using the provided notation on group, name, and version + */ + public fun dependency(notation: Any): Spec + + /** + * Create a spec that matches the provided dependency on group, name, and version + */ + public fun dependency(dependency: Dependency): Spec + + /** + * Create a spec that matches the provided closure + */ + public fun dependency(spec: Closure<*>): Spec +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt new file mode 100644 index 000000000..de403aa97 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -0,0 +1,142 @@ +package com.github.jengelman.gradle.plugins.shadow + +import com.github.jengelman.gradle.plugins.shadow.internal.JavaJarExec +import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import java.io.File +import org.gradle.api.GradleException +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.distribution.DistributionContainer +import org.gradle.api.file.CopySpec +import org.gradle.api.internal.plugins.DefaultTemplateBasedStartScriptGenerator +import org.gradle.api.plugins.ApplicationPlugin +import org.gradle.api.plugins.JavaApplication +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.Sync +import org.gradle.api.tasks.TaskProvider +import org.gradle.api.tasks.application.CreateStartScripts +import org.gradle.jvm.toolchain.JavaToolchainService + +public class ShadowApplicationPlugin : Plugin { + private val shadowJar: TaskProvider + get() = project.tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME, ShadowJar::class.java) + + private lateinit var project: Project + private lateinit var javaApplication: JavaApplication + + override fun apply(project: Project) { + this.project = project + this.javaApplication = project.extensions.getByType(JavaApplication::class.java) + + val distributions = project.extensions.getByName("distributions") as DistributionContainer + val distribution = distributions.create("shadow") + + addRunTask(project) + addCreateScriptsTask(project) + configureDistSpec(project, distribution.contents) + configureJarMainClass() + configureInstallTask(project) + } + + private fun configureJarMainClass() { + val classNameProvider = javaApplication.mainClass + shadowJar.configure { jar -> + jar.inputs.property("mainClassName", classNameProvider) + jar.doFirst { + jar.manifest.attributes["Main-Class"] = classNameProvider.get() + } + } + } + + private fun addRunTask(project: Project) { + project.tasks.register(SHADOW_RUN_TASK_NAME, JavaJarExec::class.java) { run -> + val install = project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync::class.java) + run.dependsOn(SHADOW_INSTALL_TASK_NAME) + run.mainClass.set("-jar") + run.description = "Runs this project as a JVM application using the shadow jar" + run.group = ApplicationPlugin.APPLICATION_GROUP + run.conventionMapping.map("jvmArgs") { javaApplication.applicationDefaultJvmArgs } + run.conventionMapping.map("jarFile") { + project.file("${install.get().destinationDir.path}/lib/${shadowJar.get().archiveFile.get().asFile.name}") + } + configureJavaLauncher(run) + } + } + + private fun configureJavaLauncher(run: JavaJarExec) { + val toolchain = project.extensions.getByType(JavaPluginExtension::class.java).toolchain + val service = project.extensions.getByType(JavaToolchainService::class.java) + val defaultLauncher = service.launcherFor(toolchain) + run.javaLauncher.set(defaultLauncher) + } + + private fun addCreateScriptsTask(project: Project) { + project.tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) { startScripts -> + (startScripts.unixStartScriptGenerator as DefaultTemplateBasedStartScriptGenerator).template = + project.resources.text.fromString(this::class.java.requireResourceAsText("internal/unixStartScript.txt")) + (startScripts.windowsStartScriptGenerator as DefaultTemplateBasedStartScriptGenerator).template = + project.resources.text.fromString(this::class.java.requireResourceAsText("internal/windowsStartScript.txt")) + startScripts.description = + "Creates OS specific scripts to run the project as a JVM application using the shadow jar" + startScripts.group = ApplicationPlugin.APPLICATION_GROUP + startScripts.classpath = project.files(shadowJar) + startScripts.conventionMapping.map("mainClassName") { javaApplication.mainClass.get() } + startScripts.conventionMapping.map("applicationName") { javaApplication.applicationName } + startScripts.conventionMapping.map("outputDir") { + project.layout.buildDirectory.dir("scriptsShadow").get().asFile + } + startScripts.conventionMapping.map("defaultJvmOpts") { javaApplication.applicationDefaultJvmArgs } + startScripts.inputs.files(project.objects.fileCollection().from(shadowJar)) + } + } + + private fun configureInstallTask(project: Project) { + project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync::class.java).configure { task -> + task.doFirst { + if (task.destinationDir.isDirectory) { + if (task.destinationDir.listFiles()?.isNotEmpty() == true && + (!File(task.destinationDir, "lib").isDirectory || !File(task.destinationDir, "bin").isDirectory) + ) { + throw GradleException( + "The specified installation directory '${task.destinationDir}' is neither empty nor does it contain an installation for '${javaApplication.applicationName}'.\n" + + "If you really want to install to this directory, delete it and run the install task again.\n" + + "Alternatively, choose a different installation directory.", + ) + } + } + } + task.doLast { + task.eachFile { file -> + if (file.path == "bin/${javaApplication.applicationName}") { + file.permissions { + it.unix(0x755) + } + } + } + } + } + } + + private fun configureDistSpec(project: Project, distSpec: CopySpec): CopySpec { + val startScripts = project.tasks.named(SHADOW_SCRIPTS_TASK_NAME) + return distSpec.apply { + from(project.file("src/dist")) + + into("lib") { + from(shadowJar) + from(project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME)) + } + into("bin") { + from(startScripts) + filePermissions { it.unix(493) } + } + } + } + + public companion object { + public const val SHADOW_RUN_TASK_NAME: String = "runShadow" + public const val SHADOW_SCRIPTS_TASK_NAME: String = "startShadowScripts" + public const val SHADOW_INSTALL_TASK_NAME: String = "installShadowDist" + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt new file mode 100644 index 000000000..34d62be16 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -0,0 +1,29 @@ +package com.github.jengelman.gradle.plugins.shadow + +import com.github.jengelman.gradle.plugins.shadow.tasks.KnowsTask +import org.gradle.api.GradleException +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.util.GradleVersion + +public class ShadowBasePlugin : Plugin { + override fun apply(project: Project) { + if (GradleVersion.current() < GradleVersion.version("8.3")) { + throw GradleException("This version of Shadow supports Gradle 8.3+ only. Please upgrade.") + } + + project.extensions.create(EXTENSION_NAME, ShadowExtension::class.java, project) + project.configurations.create(CONFIGURATION_NAME) + + project.tasks.register(KnowsTask.NAME, KnowsTask::class.java) { + it.group = ShadowJavaPlugin.SHADOW_GROUP + it.description = KnowsTask.DESC + } + } + + public companion object { + public const val EXTENSION_NAME: String = "shadow" + public const val CONFIGURATION_NAME: String = "shadow" + public const val COMPONENT_NAME: String = "shadow" + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt new file mode 100644 index 000000000..f76916b96 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt @@ -0,0 +1,17 @@ +package com.github.jengelman.gradle.plugins.shadow + +import org.gradle.api.Project +import org.gradle.api.publish.maven.MavenPublication + +@Deprecated("This is deprecated since 8.3.2") +public abstract class ShadowExtension(project: Project) { + private val components = project.components + + @Deprecated( + message = "configure publication using component.shadow directly.", + replaceWith = ReplaceWith("publication.from(components.getByName(\"shadow\"))"), + ) + public fun component(publication: MavenPublication) { + publication.from(components.findByName("shadow")) + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt new file mode 100644 index 000000000..d2fd40e15 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt @@ -0,0 +1,117 @@ +package com.github.jengelman.gradle.plugins.shadow + +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import javax.inject.Inject +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.attributes.Bundling +import org.gradle.api.attributes.Category +import org.gradle.api.attributes.LibraryElements +import org.gradle.api.attributes.Usage +import org.gradle.api.component.AdhocComponentWithVariants +import org.gradle.api.component.SoftwareComponentFactory +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.TaskProvider +import org.gradle.jvm.tasks.Jar +import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin + +public class ShadowJavaPlugin @Inject constructor( + private val softwareComponentFactory: SoftwareComponentFactory, +) : Plugin { + + override fun apply(project: Project) { + val shadowConfiguration = project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) + val shadowTaskProvider = configureShadowTask(project, shadowConfiguration) + + project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) { + it.extendsFrom(shadowConfiguration) + } + + val shadowRuntimeElements = project.configurations.create(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) { + it.extendsFrom(shadowConfiguration) + it.isCanBeConsumed = true + it.isCanBeResolved = false + it.attributes { attrs -> + attrs.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, Usage.JAVA_RUNTIME)) + attrs.attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category::class.java, Category.LIBRARY)) + attrs.attribute( + LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, + project.objects.named(LibraryElements::class.java, LibraryElements.JAR), + ) + attrs.attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling::class.java, Bundling.SHADOWED)) + } + it.outgoing.artifact(shadowTaskProvider) + } + + project.components.named("java", AdhocComponentWithVariants::class.java) { + it.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> + variant.mapToOptional() + } + } + + softwareComponentFactory.adhoc(ShadowBasePlugin.COMPONENT_NAME).also { shadowComponent -> + project.components.add(shadowComponent) + shadowComponent.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> + variant.mapToMavenScope("runtime") + } + } + + project.plugins.withType(JavaGradlePluginPlugin::class.java).configureEach { + // Remove the gradleApi so it isn't merged into the jar file. + // This is required because 'java-gradle-plugin' adds gradleApi() to the 'api' configuration. + // See https://github.com/gradle/gradle/blob/972c3e5c6ef990dd2190769c1ce31998a9402a79/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java#L161 + project.configurations.named(JavaPlugin.API_CONFIGURATION_NAME) { + it.dependencies.remove(project.dependencies.gradleApi()) + } + // Compile only gradleApi() to make sure the plugin can compile against Gradle API. + project.configurations.named(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME) { + it.dependencies.add(project.dependencies.gradleApi()) + } + } + } + + public companion object { + public const val SHADOW_JAR_TASK_NAME: String = "shadowJar" + public const val SHADOW_GROUP: String = "Shadow" + public const val SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME: String = "shadowRuntimeElements" + + @JvmStatic + public fun configureShadowTask( + project: Project, + shadowConfiguration: Configuration, + ): TaskProvider { + val sourceSets = project.extensions.getByType(SourceSetContainer::class.java) + val jarTask = project.tasks.named(JavaPlugin.JAR_TASK_NAME, Jar::class.java) + val taskProvider = project.tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) { shadow -> + shadow.group = SHADOW_GROUP + shadow.description = "Create a combined JAR of project and runtime dependencies" + shadow.archiveClassifier.set("all") + shadow.manifest.inheritFrom(jarTask.get().manifest) + val attrProvider = jarTask.map { it.manifest.attributes["Class-Path"] ?: "" } + val files = project.objects.fileCollection().from(shadowConfiguration) + shadow.doFirst { + if (!files.isEmpty) { + val attrs = listOf(attrProvider.get()) + files.map { it.name } + shadow.manifest.attributes["Class-Path"] = attrs.joinToString(" ").trim() + } + } + shadow.from(sourceSets.getByName("main").output) + shadow.configurations = listOfNotNull( + project.configurations.findByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) + ?: project.configurations.getByName("runtime"), + ) + shadow.exclude( + "META-INF/INDEX.LIST", + "META-INF/*.SF", + "META-INF/*.DSA", + "META-INF/*.RSA", + "module-info.class", + ) + } + project.artifacts.add(shadowConfiguration.name, taskProvider) + return taskProvider + } + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt new file mode 100644 index 000000000..f6b2c1c10 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt @@ -0,0 +1,23 @@ +package com.github.jengelman.gradle.plugins.shadow + +import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.ApplicationPlugin +import org.gradle.api.plugins.JavaPlugin + +public class ShadowPlugin : Plugin { + + override fun apply(project: Project) { + with(project.plugins) { + apply(ShadowBasePlugin::class.java) + apply(LegacyShadowPlugin::class.java) + withType(JavaPlugin::class.java) { + apply(ShadowJavaPlugin::class.java) + } + withType(ApplicationPlugin::class.java) { + apply(ShadowApplicationPlugin::class.java) + } + } + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt new file mode 100644 index 000000000..a3ba052e1 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt @@ -0,0 +1,55 @@ +package com.github.jengelman.gradle.plugins.shadow + +import org.gradle.api.GradleException + +public class ShadowStats { + private val relocations = mutableMapOf() + + public var totalTime: Long = 0 + public var jarStartTime: Long = 0 + public var jarEndTime: Long = 0 + public var jarCount: Int = 0 + public var processingJar: Boolean = false + + public inline val jarTiming: Long get() = jarEndTime - jarStartTime + public inline val totalTimeSecs: Double get() = totalTime / 1000.0 + public inline val averageTimePerJar: Double get() = totalTime / jarCount.toDouble() + public inline val averageTimeSecsPerJar: Double get() = averageTimePerJar / 1000 + + public fun relocate(src: String, dest: String) { + relocations[src] = dest + } + + public fun getRelocationString(): String { + return relocations.entries.joinToString("\n") { "${it.key} → ${it.value}" } + } + + public fun startJar() { + if (processingJar) throw GradleException("Can only time one entry at a time") + processingJar = true + jarStartTime = System.currentTimeMillis() + } + + public fun finishJar() { + if (processingJar) { + jarEndTime = System.currentTimeMillis() + jarCount++ + totalTime += jarTiming + processingJar = false + } + } + + public fun printStats() { + println(this) + } + + override fun toString(): String = buildString { + append("*******************\n") + append("GRADLE SHADOW STATS\n") + append("\n") + append("Total Jars: $jarCount (includes project)\n") + append("Total Time: ${totalTimeSecs}s [${totalTime}ms]\n") + append("Average Time/Jar: ${averageTimeSecsPerJar}s [${averageTimePerJar}ms]\n") + append("*******************") + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ZipCompressor.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ZipCompressor.kt new file mode 100644 index 000000000..c92e66474 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ZipCompressor.kt @@ -0,0 +1,5 @@ +package com.github.jengelman.gradle.plugins.shadow + +import org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory + +public interface ZipCompressor : ArchiveOutputStreamFactory diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt new file mode 100644 index 000000000..7be881543 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt @@ -0,0 +1,85 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import com.github.jengelman.gradle.plugins.shadow.DependencyFilter +import groovy.lang.Closure +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.ResolvedArtifact +import org.gradle.api.artifacts.ResolvedDependency +import org.gradle.api.file.FileCollection +import org.gradle.api.specs.Spec +import org.gradle.api.specs.Specs + +internal abstract class AbstractDependencyFilter(protected val project: Project) : DependencyFilter { + private val includeSpecs = mutableListOf>() + private val excludeSpecs = mutableListOf>() + + protected abstract fun resolve( + dependencies: Set, + includedDependencies: MutableSet, + excludedDependencies: MutableSet, + ) + + override fun resolve(configuration: FileCollection): FileCollection { + val includedDeps = mutableSetOf() + val excludedDeps = mutableSetOf() + resolve( + (configuration as Configuration).resolvedConfiguration.firstLevelModuleDependencies, + includedDeps, + excludedDeps, + ) + return project.files(configuration.files) - + project.files(excludedDeps.flatMap { it.moduleArtifacts.map(ResolvedArtifact::getFile) }) + } + + override fun resolve(configurations: Collection): FileCollection { + return configurations.map { resolve(it) } + .reduceOrNull { acc, fileCollection -> acc + fileCollection } ?: project.files() + } + + override fun exclude(spec: Spec): DependencyFilter = apply { + excludeSpecs.add(spec) + } + + override fun include(spec: Spec): DependencyFilter = apply { + includeSpecs.add(spec) + } + + override fun project(notation: Map): Spec { + return dependency(project.dependencies.project(notation)) + } + + override fun project(notation: String): Spec { + return dependency( + project.dependencies.project( + mapOf( + "path" to notation, + "configuration" to "default", + ), + ), + ) + } + + override fun dependency(notation: Any): Spec { + return dependency(project.dependencies.create(notation)) + } + + override fun dependency(dependency: Dependency): Spec { + return dependency { it: ResolvedDependency -> + (dependency.group == null || it.moduleGroup.matches(dependency.group!!.toRegex())) && + (it.moduleName.matches(dependency.name.toRegex())) && + (dependency.version == null || it.moduleVersion.matches(dependency.version!!.toRegex())) + } + } + + override fun dependency(spec: Closure<*>): Spec { + return Specs.convertClosureToSpec(spec) + } + + protected fun ResolvedDependency.isIncluded(): Boolean { + val include = includeSpecs.isEmpty() || includeSpecs.any { it.isSatisfiedBy(this) } + val exclude = excludeSpecs.isNotEmpty() && excludeSpecs.any { it.isSatisfiedBy(this) } + return include && !exclude + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt new file mode 100644 index 000000000..732ce3a60 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt @@ -0,0 +1,28 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import java.io.BufferedWriter +import java.io.IOException +import java.io.Writer +import java.util.Date +import java.util.Properties + +internal class CleanProperties : Properties() { + @Throws(IOException::class) + override fun store(writer: Writer, comments: String) { + super.store(StripCommentsWithTimestampBufferedWriter(writer), comments) + } + + private class StripCommentsWithTimestampBufferedWriter(out: Writer) : BufferedWriter(out) { + private val lengthOfExpectedTimestamp = ("#" + Date().toString()).length + + @Throws(IOException::class) + override fun write(str: String) { + if (couldBeCommentWithTimestamp(str)) return + super.write(str) + } + + private fun couldBeCommentWithTimestamp(str: String?): Boolean { + return str != null && str.startsWith("#") && str.length == lengthOfExpectedTimestamp + } + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt new file mode 100644 index 000000000..da5f4e446 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt @@ -0,0 +1,18 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import org.gradle.api.Project +import org.gradle.api.artifacts.ResolvedDependency + +internal class DefaultDependencyFilter(project: Project) : AbstractDependencyFilter(project) { + override fun resolve( + dependencies: Set, + includedDependencies: MutableSet, + excludedDependencies: MutableSet, + ) { + dependencies.forEach { + if (if (it.isIncluded()) includedDependencies.add(it) else excludedDependencies.add(it)) { + resolve(it.children, includedDependencies, excludedDependencies) + } + } + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt new file mode 100644 index 000000000..74bb0a531 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt @@ -0,0 +1,36 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import com.github.jengelman.gradle.plugins.shadow.tasks.InheritManifest +import org.gradle.api.Action +import org.gradle.api.internal.file.FileResolver +import org.gradle.api.java.archives.Manifest +import org.gradle.api.java.archives.internal.DefaultManifest +import org.gradle.api.java.archives.internal.DefaultManifestMergeSpec + +internal class DefaultInheritManifest( + private val fileResolver: FileResolver, + private val internalManifest: Manifest = DefaultManifest(fileResolver), +) : InheritManifest, + Manifest by internalManifest { + private val inheritMergeSpecs = mutableListOf() + + override fun inheritFrom(vararg inheritPaths: Any): InheritManifest = apply { + inheritFrom(inheritPaths, action = null) + } + + override fun inheritFrom(vararg inheritPaths: Any, action: Action?): InheritManifest = apply { + val mergeSpec = DefaultManifestMergeSpec() + mergeSpec.from(inheritPaths) + inheritMergeSpecs.add(mergeSpec) + action?.execute(this) + } + + override fun getEffectiveManifest(): Manifest { + var base = DefaultManifest(fileResolver) + inheritMergeSpecs.forEach { + base = it.merge(base, fileResolver) + } + base.from(internalManifest) + return base.effectiveManifest + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt new file mode 100644 index 000000000..a8f52c065 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt @@ -0,0 +1,25 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import com.github.jengelman.gradle.plugins.shadow.ZipCompressor +import java.io.File +import org.apache.tools.zip.Zip64Mode +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.UncheckedIOException + +internal class DefaultZipCompressor( + allowZip64Mode: Boolean, + private val entryCompressionMethod: Int, +) : ZipCompressor { + private val zip64Mode: Zip64Mode = if (allowZip64Mode) Zip64Mode.AsNeeded else Zip64Mode.Never + + override fun createArchiveOutputStream(destination: File): ZipOutputStream { + try { + return ZipOutputStream(destination).apply { + setUseZip64(zip64Mode) + setMethod(entryCompressionMethod) + } + } catch (e: Exception) { + throw UncheckedIOException("Unable to create ZIP output stream for file $destination.", e) + } + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt new file mode 100644 index 000000000..62b0e0bfd --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt @@ -0,0 +1,19 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.JavaExec +import org.gradle.api.tasks.TaskAction + +internal abstract class JavaJarExec : JavaExec() { + + @get:InputFile + val jarFile: RegularFileProperty = objectFactory.fileProperty() + + @TaskAction + override fun exec() { + val allArgs = listOf(jarFile.get().asFile.path) + args + setArgs(allArgs) + super.exec() + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt new file mode 100644 index 000000000..2b3881cce --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt @@ -0,0 +1,26 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import org.gradle.api.Project +import org.gradle.api.artifacts.ResolvedDependency + +internal class MinimizeDependencyFilter(project: Project) : AbstractDependencyFilter(project) { + override fun resolve( + dependencies: Set, + includedDependencies: MutableSet, + excludedDependencies: MutableSet, + ) { + dependencies.forEach { + val flag = it.isIncluded() && !isParentExcluded(excludedDependencies, it) + if (if (flag) includedDependencies.add(it) else excludedDependencies.add(it)) { + resolve(it.children, includedDependencies, excludedDependencies) + } + } + } + + private fun isParentExcluded( + excludedDependencies: Set, + dependency: ResolvedDependency, + ): Boolean { + return excludedDependencies.any { dependency.parents.contains(it) } + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt new file mode 100644 index 000000000..17747b56c --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt @@ -0,0 +1,71 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext +import com.github.jengelman.gradle.plugins.shadow.relocation.RelocatePathContext +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction +import java.util.regex.Pattern +import org.objectweb.asm.commons.Remapper + +/** + * Modified from org.apache.maven.plugins.shade.DefaultShader.java#RelocatorRemapper + * + * @author John Engelman + */ +internal class RelocatorRemapper( + internal val relocators: List, + internal val stats: ShadowStats, +) : Remapper() { + private val classPattern: Pattern = Pattern.compile("(\\[*)?L(.+)") + + fun hasRelocators(): Boolean = relocators.isNotEmpty() + + override fun mapValue(value: Any): Any { + if (value is String) { + return mapStringValue(value, true) + } + return super.mapValue(value) + } + + override fun map(internalName: String): String { + return mapStringValue(internalName, false) + } + + fun mapPath(path: String): String { + return map(path.substring(0, path.indexOf("."))) + } + + fun mapPath(path: ShadowCopyAction.RelativeArchivePath): String { + return mapPath(path.pathString) + } + + private fun mapStringValue(value: String, relocatePath: Boolean): String { + var name = value + var finalValue = name + + var prefix = "" + var suffix = "" + + val m = classPattern.matcher(name) + if (m.matches()) { + prefix = m.group(1) + "L" + suffix = "" + name = m.group(2) + } + + for (r in relocators) { + if (r.canRelocateClass(name)) { + val classContext = RelocateClassContext(name, stats) + finalValue = prefix + r.relocateClass(classContext) + suffix + break + } else if (r.canRelocatePath(name)) { + if (!relocatePath) continue + val pathContext = RelocatePathContext(name, stats) + finalValue = prefix + r.relocatePath(pathContext) + suffix + break + } + } + return finalValue + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec.kt new file mode 100644 index 000000000..817959f18 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ShadowSpec.kt @@ -0,0 +1,71 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import com.github.jengelman.gradle.plugins.shadow.DependencyFilter +import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator +import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer +import java.lang.reflect.InvocationTargetException +import org.gradle.api.Action +import org.gradle.api.file.CopySpec + +internal interface ShadowSpec : CopySpec { + fun minimize(): ShadowSpec + + fun minimize(action: Action?): ShadowSpec + + fun dependencies(action: Action?): ShadowSpec + + @Throws( + InstantiationException::class, + IllegalAccessException::class, + NoSuchMethodException::class, + InvocationTargetException::class, + ) + fun transform(clazz: Class): ShadowSpec + + @Throws( + InstantiationException::class, + IllegalAccessException::class, + NoSuchMethodException::class, + InvocationTargetException::class, + ) + fun transform(clazz: Class, action: Action?): ShadowSpec + + fun transform(transformer: Transformer): ShadowSpec + + fun mergeServiceFiles(): ShadowSpec + + fun mergeServiceFiles(rootPath: String): ShadowSpec + + fun mergeServiceFiles(action: Action?): ShadowSpec + + fun mergeGroovyExtensionModules(): ShadowSpec + + fun append(resourcePath: String): ShadowSpec + + fun relocate(pattern: String, destination: String): ShadowSpec + + fun relocate(pattern: String, destination: String, action: Action?): ShadowSpec + + fun relocate(relocator: Relocator): ShadowSpec + + @Throws( + InstantiationException::class, + IllegalAccessException::class, + NoSuchMethodException::class, + InvocationTargetException::class, + ) + fun relocate(clazz: Class): ShadowSpec + + @Throws( + InstantiationException::class, + IllegalAccessException::class, + NoSuchMethodException::class, + InvocationTargetException::class, + ) + fun relocate(clazz: Class, action: Action?): ShadowSpec + + val stats: ShadowStats +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt new file mode 100644 index 000000000..91bd61633 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt @@ -0,0 +1,86 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import java.io.File +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.ProjectDependency +import org.gradle.api.artifacts.SelfResolvingDependency +import org.gradle.api.file.FileCollection +import org.gradle.api.tasks.InputFiles +import org.vafer.jdependency.Clazzpath +import org.vafer.jdependency.ClazzpathUnit + +/** Tracks unused classes in the project classpath. */ +internal class UnusedTracker private constructor( + classDirs: Iterable, + classJars: FileCollection, + @InputFiles val toMinimize: FileCollection, +) { + private val projectUnits: List + private val cp = Clazzpath() + + init { + projectUnits = classDirs.map { cp.addClazzpathUnit(it) } + classJars.map { cp.addClazzpathUnit(it) } + } + + fun findUnused(): Set { + val unused = cp.clazzes.toMutableSet() + for (cpu in projectUnits) { + unused.removeAll(cpu.clazzes) + unused.removeAll(cpu.transitiveDependencies) + } + return unused.map { it.name }.toSet() + } + + fun addDependency(jarOrDir: File) { + if (toMinimize.contains(jarOrDir)) { + cp.addClazzpathUnit(jarOrDir) + } + } + + companion object { + fun forProject( + apiJars: FileCollection, + sourceSetsClassesDirs: Iterable, + toMinimize: FileCollection, + ): UnusedTracker { + return UnusedTracker(sourceSetsClassesDirs, apiJars, toMinimize) + } + + fun getApiJarsFromProject(project: Project): FileCollection { + val apiDependencies = project.configurations.findByName("api")?.dependencies + ?: return project.files() + val runtimeConfiguration = project.configurations.findByName("runtimeClasspath") + ?: project.configurations.getByName("runtime") + val apiJars = mutableListOf() + + apiDependencies.forEach { dep -> + when (dep) { + is ProjectDependency -> { + apiJars.addAll(getApiJarsFromProject(dep.dependencyProject)) + addJar(runtimeConfiguration, dep, apiJars) + } + is SelfResolvingDependency -> apiJars.addAll(dep.resolve()) + else -> { + addJar(runtimeConfiguration, dep, apiJars) + runtimeConfiguration.find { it.name.startsWith("${dep.name}-") }?.let { apiJars.add(it) } + } + } + } + return project.files(apiJars) + } + + private fun isProjectDependencyFile(file: File, dep: Dependency): Boolean { + val fileName = file.name + val dependencyName = dep.name + return fileName == "$dependencyName.jar" || + (fileName.startsWith("$dependencyName-") && fileName.endsWith(".jar")) + } + + private fun addJar(config: Configuration, dep: Dependency, result: MutableList) { + val file = config.find { isProjectDependencyFile(it, dep) } + file?.let { result.add(it) } + } + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt new file mode 100644 index 000000000..273ad933b --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -0,0 +1,22 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import java.io.InputStream + +internal inline fun runOrThrow( + error: (e: Exception) -> Nothing = { throw RuntimeException(it) }, + block: () -> R, +): R { + return try { + block() + } catch (e: Exception) { + error(e) + } +} + +internal fun Class<*>.requireResourceAsText(name: String): String { + return requireResourceAsStream(name).bufferedReader().readText() +} + +private fun Class<*>.requireResourceAsStream(name: String): InputStream { + return getResourceAsStream(name) ?: error("Resource $name not found.") +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt new file mode 100644 index 000000000..759e48eb4 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt @@ -0,0 +1,13 @@ +package com.github.jengelman.gradle.plugins.shadow.legacy + +import org.gradle.api.Plugin +import org.gradle.api.Project + +/** + * Empty plugin to still have the [com.github.johnrengelman.shadow](https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow) plugin applied. + * + * This allows older build logic to keep on working as if that old plugin ID was applied. + */ +public class LegacyShadowPlugin : Plugin { + override fun apply(project: Project): Unit = Unit +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt new file mode 100644 index 000000000..e78d7a777 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt @@ -0,0 +1,10 @@ +package com.github.jengelman.gradle.plugins.shadow.relocation + +/** + * Marks that a given instance of [Relocator] is compatible with the Gradle build cache. + * In other words, it has its appropriate inputs annotated so that Gradle can consider them when + * determining the cache key. + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.CLASS) +public annotation class CacheableRelocator diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt new file mode 100644 index 000000000..b5ff517fa --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt @@ -0,0 +1,8 @@ +package com.github.jengelman.gradle.plugins.shadow.relocation + +import com.github.jengelman.gradle.plugins.shadow.ShadowStats + +public data class RelocateClassContext( + val className: String, + val stats: ShadowStats, +) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt new file mode 100644 index 000000000..d5df66f0c --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt @@ -0,0 +1,8 @@ +package com.github.jengelman.gradle.plugins.shadow.relocation + +import com.github.jengelman.gradle.plugins.shadow.ShadowStats + +public data class RelocatePathContext( + val path: String, + val stats: ShadowStats, +) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt new file mode 100644 index 000000000..b57e964bf --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt @@ -0,0 +1,25 @@ + +package com.github.jengelman.gradle.plugins.shadow.relocation + +/** + * Modified from org.apache.maven.plugins.shade.relocation.Relocator.java + * + * @author Jason van Zyl + * @author John Engelman + */ +public interface Relocator { + public fun canRelocatePath(path: String): Boolean + + public fun relocatePath(context: RelocatePathContext): String + + public fun canRelocateClass(className: String): Boolean + + public fun relocateClass(context: RelocateClassContext): String + + public fun applyToSourceContent(sourceContent: String): String + + public companion object { + @JvmField + public val ROLE: String = Relocator::class.java.name + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt new file mode 100644 index 000000000..6fee7a093 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -0,0 +1,150 @@ +package com.github.jengelman.gradle.plugins.shadow.relocation + +import java.util.regex.Pattern +import org.codehaus.plexus.util.SelectorUtils +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional + +/** + * Modified from org.apache.maven.plugins.shade.relocation.SimpleRelocator.java + * + * @author Jason van Zyl + * @author Mauro Talevi + * @author John Engelman + */ +@CacheableRelocator +public class SimpleRelocator @JvmOverloads constructor( + patt: String?, + shadedPattern: String?, + includes: List?, + excludes: List?, + private val rawString: Boolean = false, +) : Relocator { + + @Input + @Optional + public val pattern: String? + + @Input + @Optional + public val pathPattern: String + + @Input + @Optional + public val shadedPattern: String? + + @Input + @Optional + public val shadedPathPattern: String + + @Input + @Optional + public val includes: MutableSet = mutableSetOf() + + @Input + @Optional + public val excludes: MutableSet = mutableSetOf() + + init { + if (rawString) { + this.pathPattern = patt.orEmpty() + this.shadedPathPattern = shadedPattern.orEmpty() + this.pattern = null + this.shadedPattern = null + } else { + this.pattern = patt?.replace('/', '.').orEmpty() + this.pathPattern = patt?.replace('.', '/').orEmpty() + this.shadedPattern = shadedPattern?.replace('/', '.') ?: "hidden.$pattern" + this.shadedPathPattern = shadedPattern?.replace('.', '/') ?: "hidden/$pathPattern" + } + this.includes += normalizePatterns(includes) + this.excludes += normalizePatterns(excludes) + } + + public fun include(pattern: String): SimpleRelocator = apply { + includes.addAll(normalizePatterns(listOf(pattern))) + } + + public fun exclude(pattern: String): SimpleRelocator = apply { + excludes.addAll(normalizePatterns(listOf(pattern))) + } + + override fun canRelocatePath(path: String): Boolean { + if (rawString) { + return Pattern.compile(pathPattern).matcher(path).find() + } + if (path.length < pathPattern.length) { + return false + } + var modifiedPath = path + if (path.endsWith(".class")) { + if (path.length == 6) { + return false + } + modifiedPath = path.substring(0, path.length - 6) + } + val pathStartsWithPattern = if (modifiedPath[0] == '/') { + modifiedPath.startsWith(pathPattern, 1) + } else { + modifiedPath.startsWith(pathPattern) + } + return pathStartsWithPattern && + includes.isMatching(modifiedPath, true) && + !excludes.isMatching(modifiedPath, false) + } + + override fun canRelocateClass(className: String): Boolean { + return !rawString && className.indexOf('/') < 0 && canRelocatePath(className.replace('.', '/')) + } + + override fun relocatePath(context: RelocatePathContext): String { + val path = context.path + context.stats.relocate(pathPattern, shadedPathPattern) + return if (rawString) { + path.replace(pathPattern.toRegex(), shadedPathPattern) + } else { + path.replaceFirst(pathPattern, shadedPathPattern) + } + } + + override fun relocateClass(context: RelocateClassContext): String { + val clazz = context.className + context.stats.relocate(pathPattern, shadedPathPattern) + return clazz.replaceFirst(pattern!!.toRegex(), shadedPattern!!) + } + + override fun applyToSourceContent(sourceContent: String): String { + return if (rawString) { + sourceContent + } else { + sourceContent.replace("\\b$pattern".toRegex(), shadedPattern!!) + } + } + + private fun normalizePatterns(patterns: Collection?): Set { + val normalized = mutableSetOf() + patterns?.forEach { pattern -> + if (pattern.startsWith(SelectorUtils.REGEX_HANDLER_PREFIX)) { + normalized.add(pattern) + } else { + val classPattern = pattern.replace('.', '/') + normalized.add(classPattern) + if (classPattern.endsWith("/*")) { + val packagePattern = classPattern.substring(0, classPattern.lastIndexOf('/')) + normalized.add(packagePattern) + } + } + } + return normalized + } + + private fun Set.isMatching(path: String, default: Boolean): Boolean { + if (isNotEmpty()) { + forEach { pattern -> + if (SelectorUtils.matchPath(pattern, path, "/", true)) return true + } + return false + } + return default + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt new file mode 100644 index 000000000..ca9672115 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt @@ -0,0 +1,9 @@ +package com.github.jengelman.gradle.plugins.shadow.tasks + +import org.gradle.api.Action +import org.gradle.api.java.archives.Manifest + +public interface InheritManifest : Manifest { + public fun inheritFrom(vararg inheritPaths: Any): InheritManifest + public fun inheritFrom(vararg inheritPaths: Any, action: Action?): InheritManifest +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt new file mode 100644 index 000000000..4e655630b --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt @@ -0,0 +1,18 @@ +package com.github.jengelman.gradle.plugins.shadow.tasks + +import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.TaskAction + +public abstract class KnowsTask : DefaultTask() { + @TaskAction + public fun knows() { + logger.info("No, The Shadow Knows....") + logger.info(this::class.java.requireResourceAsText("/shadowBanner.txt")) + } + + public companion object { + public const val NAME: String = "knows" + public const val DESC: String = "Do you know who knows?" + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt new file mode 100644 index 000000000..fe63f3e31 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -0,0 +1,416 @@ +package com.github.jengelman.gradle.plugins.shadow.tasks + +import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import com.github.jengelman.gradle.plugins.shadow.ZipCompressor +import com.github.jengelman.gradle.plugins.shadow.internal.RelocatorRemapper +import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer +import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext +import java.io.File +import java.io.InputStream +import java.io.OutputStream +import java.util.GregorianCalendar +import java.util.zip.ZipException +import kotlin.io.path.Path +import kotlin.io.path.extension +import kotlin.io.path.nameWithoutExtension +import org.apache.log4j.LogManager +import org.apache.tools.zip.UnixStat +import org.apache.tools.zip.Zip64RequiredException +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipFile +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.GradleException +import org.gradle.api.UncheckedIOException +import org.gradle.api.file.FileCopyDetails +import org.gradle.api.file.FilePermissions +import org.gradle.api.file.FileTreeElement +import org.gradle.api.file.RelativePath +import org.gradle.api.internal.DocumentationRegistry +import org.gradle.api.internal.file.CopyActionProcessingStreamAction +import org.gradle.api.internal.file.DefaultFilePermissions +import org.gradle.api.internal.file.DefaultFileTreeElement +import org.gradle.api.internal.file.copy.CopyAction +import org.gradle.api.internal.file.copy.CopyActionProcessingStream +import org.gradle.api.internal.file.copy.FileCopyDetailsInternal +import org.gradle.api.tasks.WorkResult +import org.gradle.api.tasks.WorkResults +import org.gradle.api.tasks.bundling.Zip +import org.gradle.api.tasks.util.PatternSet +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.commons.ClassRemapper + +public class ShadowCopyAction internal constructor( + private val zipFile: File, + private val compressor: ZipCompressor, + private val documentationRegistry: DocumentationRegistry, + private val encoding: String?, + private val transformers: List, + private val relocators: List, + private val patternSet: PatternSet, + private val stats: ShadowStats, + private val preserveFileTimestamps: Boolean, + private val minimizeJar: Boolean, + private val unusedTracker: UnusedTracker, +) : CopyAction { + private val logger = LogManager.getLogger(this::class.java) + + override fun execute(stream: CopyActionProcessingStream): WorkResult { + val unusedClasses: Set = if (minimizeJar) { + stream.process( + object : BaseStreamAction() { + override fun visitFile(fileDetails: FileCopyDetails) { + if (fileDetails.isArchive) { + unusedTracker.addDependency(fileDetails.file) + } + } + }, + ) + unusedTracker.findUnused() + } else { + emptySet() + } + + val zipOutStr = try { + compressor.createArchiveOutputStream(zipFile) as ZipOutputStream + } catch (e: Exception) { + throw GradleException("Could not create ZIP '$zipFile'", e) + } + + try { + zipOutStr.use { outputStream -> + try { + val action = + StreamAction(outputStream, encoding, transformers, relocators, patternSet, unusedClasses, stats) + stream.process(action) + processTransformers(outputStream) + } catch (e: Exception) { + throw e + } + } + } catch (e: UncheckedIOException) { + if (e.cause is Zip64RequiredException) { + val message = (e.cause as Zip64RequiredException).message + throw Zip64RequiredException( + "$message\n\nTo build this archive, please enable the zip64 extension." + + "\nSee: ${documentationRegistry.getDslRefForProperty(Zip::class.java, "zip64")}", + ) + } + } + return WorkResults.didWork(true) + } + + private fun processTransformers(stream: ZipOutputStream) { + transformers.forEach { transformer -> + if (transformer.hasTransformedResource()) { + transformer.modifyOutputStream(stream, preserveFileTimestamps) + } + } + } + + private fun getArchiveTimeFor(timestamp: Long): Long { + return if (preserveFileTimestamps) timestamp else CONSTANT_TIME_FOR_ZIP_ENTRIES + } + + private fun setArchiveTimes(zipEntry: ZipEntry): ZipEntry { + if (!preserveFileTimestamps) { + zipEntry.time = CONSTANT_TIME_FOR_ZIP_ENTRIES + } + return zipEntry + } + + public abstract class BaseStreamAction : CopyActionProcessingStreamAction { + override fun processFile(details: FileCopyDetailsInternal) { + if (details.isDirectory) { + visitDir(details) + } else { + visitFile(details) + } + } + + protected open fun visitDir(dirDetails: FileCopyDetails): Unit = Unit + + protected abstract fun visitFile(fileDetails: FileCopyDetails) + + protected val FileCopyDetails.isArchive: Boolean get() = path.endsWith(".jar") + + protected val FileCopyDetails.isClass: Boolean get() = path.endsWith(".class") + } + + private inner class StreamAction( + private val zipOutStr: ZipOutputStream, + encoding: String?, + private val transformers: List, + private val relocators: List, + private val patternSet: PatternSet, + private val unused: Set, + private val stats: ShadowStats, + ) : BaseStreamAction() { + + private val remapper = RelocatorRemapper(relocators, stats) + private val visitedFiles = mutableSetOf() + + init { + if (encoding != null) { + this.zipOutStr.setEncoding(encoding) + } + } + + private fun recordVisit(path: RelativePath): Boolean { + return visitedFiles.add(path.pathString) + } + + override fun visitFile(fileDetails: FileCopyDetails) { + if (!fileDetails.isArchive) { + try { + val isClass = fileDetails.isClass + if (!remapper.hasRelocators() || !isClass) { + if (!isTransformable(fileDetails)) { + val mappedPath = remapper.map(fileDetails.relativePath.pathString) + val archiveEntry = ZipEntry(mappedPath) + archiveEntry.time = getArchiveTimeFor(fileDetails.lastModified) + archiveEntry.unixMode = UnixStat.FILE_FLAG or fileDetails.permissions.toUnixNumeric() + zipOutStr.putNextEntry(archiveEntry) + fileDetails.copyTo(zipOutStr) + zipOutStr.closeEntry() + } else { + transform(fileDetails) + } + } else if (isClass && !isUnused(fileDetails.path)) { + remapClass(fileDetails) + } + recordVisit(fileDetails.relativePath) + } catch (e: Exception) { + throw GradleException("Could not add $fileDetails to ZIP '$zipFile'.", e) + } + } else { + processArchive(fileDetails) + } + } + + private fun processArchive(fileDetails: FileCopyDetails) { + stats.startJar() + ZipFile(fileDetails.file).use { archive -> + val archiveElements = archive.entries.toList().map { ArchiveFileTreeElement(RelativeArchivePath(it)) } + val patternSpec = patternSet.asSpec + val filteredArchiveElements = + archiveElements.filter { patternSpec.isSatisfiedBy(it.asFileTreeElement()) } + filteredArchiveElements.forEach { archiveElement -> + if (archiveElement.relativePath.isFile) { + visitArchiveFile(archiveElement, archive) + } + } + } + stats.finishJar() + } + + private fun visitArchiveDirectory(archiveDir: RelativeArchivePath) { + if (recordVisit(archiveDir)) { + zipOutStr.putNextEntry(archiveDir.entry) + zipOutStr.closeEntry() + } + } + + private fun visitArchiveFile(archiveFile: ArchiveFileTreeElement, archive: ZipFile) { + val archiveFilePath = archiveFile.relativePath + if (archiveFile.isClassFile || !isTransformable(archiveFile)) { + if (recordVisit(archiveFilePath) && !isUnused(archiveFilePath.entry.name)) { + if (!remapper.hasRelocators() || !archiveFile.isClassFile) { + copyArchiveEntry(archiveFilePath, archive) + } else { + remapClass(archiveFilePath, archive) + } + } + } else { + transform(archiveFile, archive) + } + } + + private fun addParentDirectories(file: RelativeArchivePath?) { + file?.let { + addParentDirectories(it.parent) + if (!it.isFile) { + visitArchiveDirectory(it) + } + } + } + + private fun isUnused(classPath: String): Boolean { + val className = Path(classPath).nameWithoutExtension + .replace('/', '.') + val result = unused.contains(className) + if (result) { + logger.debug("Dropping unused class: $className") + } + return result + } + + private fun remapClass(file: RelativeArchivePath, archive: ZipFile) { + if (file.isClassFile) { + val zipEntry = setArchiveTimes(ZipEntry(remapper.mapPath(file) + ".class")) + addParentDirectories(RelativeArchivePath(zipEntry)) + remapClass(archive.getInputStream(file.entry), file.pathString, file.entry.time) + } + } + + private fun remapClass(fileCopyDetails: FileCopyDetails) { + if (Path(fileCopyDetails.name).extension == "class") { + fileCopyDetails.file.inputStream().use { inputStream -> + remapClass( + inputStream, + fileCopyDetails.path, + fileCopyDetails.lastModified, + ) + } + } + } + + private fun remapClass(classInputStream: InputStream, path: String, lastModified: Long) { + val cw = ClassWriter(0) + val cv = ClassRemapper(cw, remapper) + + classInputStream.use { + try { + ClassReader(it).accept(cv, ClassReader.EXPAND_FRAMES) + } catch (ise: Throwable) { + throw GradleException("Error in ASM processing class $path", ise) + } + } + + val renamedClass = cw.toByteArray() + val multiReleasePrefix = "^META-INF/versions/\\d+/".toRegex().find(path)?.value.orEmpty() + val mappedName = multiReleasePrefix + remapper.mapPath(path.replace(multiReleasePrefix, "")) + + renamedClass.inputStream().use { bis -> + try { + val archiveEntry = ZipEntry("$mappedName.class") + archiveEntry.time = getArchiveTimeFor(lastModified) + zipOutStr.putNextEntry(archiveEntry) + bis.copyTo(zipOutStr) + zipOutStr.closeEntry() + } catch (ignored: ZipException) { + } + } + } + + private fun copyArchiveEntry(archiveFile: RelativeArchivePath, archive: ZipFile) { + val mappedPath = remapper.map(archiveFile.entry.name) + val entry = ZipEntry(mappedPath) + entry.time = getArchiveTimeFor(archiveFile.entry.time) + val mappedFile = RelativeArchivePath(entry) + addParentDirectories(mappedFile) + zipOutStr.putNextEntry(mappedFile.entry) + archive.getInputStream(archiveFile.entry).copyTo(zipOutStr) + zipOutStr.closeEntry() + } + + override fun visitDir(dirDetails: FileCopyDetails) { + try { + val path = dirDetails.relativePath.pathString + "/" + val archiveEntry = ZipEntry(path) + archiveEntry.time = getArchiveTimeFor(dirDetails.lastModified) + archiveEntry.unixMode = UnixStat.DIR_FLAG or dirDetails.permissions.toUnixNumeric() + zipOutStr.putNextEntry(archiveEntry) + zipOutStr.closeEntry() + recordVisit(dirDetails.relativePath) + } catch (e: Exception) { + throw GradleException("Could not add $dirDetails to ZIP '$zipFile'.", e) + } + } + + private fun transform(element: ArchiveFileTreeElement, archive: ZipFile) { + transformAndClose(element, archive.getInputStream(element.relativePath.entry)) + } + + private fun transform(details: FileCopyDetails) { + transformAndClose(details, details.file.inputStream()) + } + + private fun transformAndClose(element: FileTreeElement, inputStream: InputStream) { + inputStream.use { + val mappedPath = remapper.map(element.relativePath.pathString) + transformers.find { it.canTransformResource(element) } + ?.transform( + TransformerContext.builder() + .path(mappedPath) + .inputStream(it) + .relocators(relocators) + .stats(stats) + .build(), + ) + } + } + + private fun isTransformable(element: FileTreeElement): Boolean { + return transformers.any { it.canTransformResource(element) } + } + } + + public inner class RelativeArchivePath(public val entry: ZipEntry) : + RelativePath( + !entry.isDirectory, + *entry.name.split("/").toTypedArray(), + ) { + + public val isClassFile: Boolean + get() = lastName.endsWith(".class") + + override fun getParent(): RelativeArchivePath { + return if (segments.isEmpty() || segments.size == 1) { + // TODO: the return type must be non-nullable + null!! + } else { + val path = segments.dropLast(1).joinToString("/") + "/" + RelativeArchivePath(setArchiveTimes(ZipEntry(path))) + } + } + } + + public class ArchiveFileTreeElement(private val archivePath: RelativeArchivePath) : FileTreeElement { + + public val isClassFile: Boolean + get() = archivePath.isClassFile + + override fun getFile(): File = error("Not supported") + + override fun isDirectory(): Boolean = archivePath.entry.isDirectory + + override fun getLastModified(): Long = archivePath.entry.lastModifiedDate.time + + override fun getSize(): Long = archivePath.entry.size + + override fun open(): InputStream = error("Not supported") + + override fun copyTo(outputStream: OutputStream) {} + + override fun copyTo(file: File): Boolean = false + + override fun getName(): String = archivePath.pathString + + override fun getPath(): String = archivePath.lastName + + override fun getRelativePath(): RelativeArchivePath = archivePath + + override fun getMode(): Int = archivePath.entry.unixMode + + override fun getPermissions(): FilePermissions = DefaultFilePermissions(mode) + + public fun asFileTreeElement(): FileTreeElement { + // TODO: the param types must be non-nullable + return DefaultFileTreeElement( + null!!, + RelativePath(!isDirectory, *archivePath.segments), + null!!, + null!!, + ) + } + } + + public companion object { + @JvmField + public val CONSTANT_TIME_FOR_ZIP_ENTRIES: Long = + GregorianCalendar(1980, 1, 1, 0, 0, 0).timeInMillis + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt new file mode 100644 index 000000000..6e2001fc3 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -0,0 +1,310 @@ +package com.github.jengelman.gradle.plugins.shadow.tasks + +import com.github.jengelman.gradle.plugins.shadow.DependencyFilter +import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import com.github.jengelman.gradle.plugins.shadow.ZipCompressor +import com.github.jengelman.gradle.plugins.shadow.internal.DefaultDependencyFilter +import com.github.jengelman.gradle.plugins.shadow.internal.DefaultInheritManifest +import com.github.jengelman.gradle.plugins.shadow.internal.DefaultZipCompressor +import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter +import com.github.jengelman.gradle.plugins.shadow.internal.ShadowSpec +import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker +import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker.Companion.getApiJarsFromProject +import com.github.jengelman.gradle.plugins.shadow.internal.runOrThrow +import com.github.jengelman.gradle.plugins.shadow.relocation.CacheableRelocator +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator +import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.CacheableTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer +import java.util.concurrent.Callable +import java.util.jar.JarFile +import java.util.zip.ZipOutputStream +import org.gradle.api.Action +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DuplicatesStrategy +import org.gradle.api.file.FileCollection +import org.gradle.api.internal.DocumentationRegistry +import org.gradle.api.internal.file.FileResolver +import org.gradle.api.internal.file.copy.CopyAction +import org.gradle.api.internal.file.copy.DefaultCopySpec +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.bundling.Jar +import org.gradle.api.tasks.bundling.ZipEntryCompression +import org.gradle.api.tasks.util.PatternSet +import org.jetbrains.annotations.NotNull + +@CacheableTask +public abstract class ShadowJar : + Jar(), + ShadowSpec { + private val _transformers = mutableListOf() + private val _relocators = mutableListOf() + private val _configurations = mutableListOf() + private var minimizeJar = false + private val dependencyFilterForMinimize = MinimizeDependencyFilter(project) + private var _toMinimize: FileCollection? = null + private var _apiJars: FileCollection? = null + private var _sourceSetsClassesDirs: FileCollection? = null + + init { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + manifest = DefaultInheritManifest(services.get(FileResolver::class.java)) + + inputs.property("minimize") { minimizeJar } + outputs.doNotCacheIf("Has one or more transforms or relocators that are not cacheable") { + _transformers.any { !isCacheableTransform(it::class.java) } || + _relocators.any { !isCacheableRelocator(it::class.java) } + } + } + + public var transformers: List + @Nested get() = _transformers + set(value) { + _transformers.clear() + _transformers.addAll(value) + } + + public var relocators: List + @Nested get() = _relocators + set(value) { + _relocators.clear() + _relocators.addAll(value) + } + + public var configurations: List + @Classpath get() = _configurations + set(value) { + _configurations.clear() + _configurations.addAll(value) + } + + @get:Input + public var enableRelocation: Boolean = false + + @get:Input + public var relocationPrefix: String = "shadow" + + @get:Internal + public var dependencyFilter: DependencyFilter = DefaultDependencyFilter(project) + + @get:Internal + override val stats: ShadowStats = ShadowStats() + + @get:Internal + public val internalCompressor: ZipCompressor + get() { + return when (entryCompression) { + ZipEntryCompression.DEFLATED -> DefaultZipCompressor(isZip64, ZipOutputStream.DEFLATED) + ZipEntryCompression.STORED -> DefaultZipCompressor(isZip64, ZipOutputStream.STORED) + else -> throw IllegalArgumentException("Unknown Compression type $entryCompression") + } + } + + @get:Classpath + public val toMinimize: FileCollection + get() { + if (_toMinimize == null) { + _toMinimize = if (minimizeJar) { + dependencyFilterForMinimize.resolve(_configurations).minus(apiJars) + } else { + project.objects.fileCollection() + } + } + return _toMinimize!! + } + + @get:Classpath + public val apiJars: FileCollection + get() { + if (_apiJars == null) { + _apiJars = if (minimizeJar) { + getApiJarsFromProject(project) + } else { + project.objects.fileCollection() + } + } + return _apiJars!! + } + + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + public val sourceSetsClassesDirs: FileCollection + get() { + if (_sourceSetsClassesDirs == null) { + val allClassesDirs = project.objects.fileCollection() + if (minimizeJar) { + for (sourceSet in project.extensions.getByType(SourceSetContainer::class.java)) { + val classesDirs = sourceSet.output.classesDirs + allClassesDirs.from(classesDirs) + } + } + _sourceSetsClassesDirs = allClassesDirs.filter { file -> file.isDirectory } + } + return _sourceSetsClassesDirs!! + } + + @get:Classpath + public val includedDependencies: ConfigurableFileCollection + get() = project.files( + Callable { + dependencyFilter.resolve(_configurations) + }, + ) + + @get:Internal + public val rootPatternSet: PatternSet + get() { + return (mainSpec.buildRootResolver() as DefaultCopySpec.DefaultCopySpecResolver).patternSet + } + + override fun minimize(): ShadowJar = apply { + minimizeJar = true + } + + override fun minimize(action: Action?): ShadowJar = apply { + minimize() + action?.execute(dependencyFilterForMinimize) + } + + override fun getManifest(): InheritManifest { + return super.getManifest() as InheritManifest + } + + @NotNull + override fun createCopyAction(): CopyAction { + val documentationRegistry = services.get(DocumentationRegistry::class.java) + val unusedTracker = if (minimizeJar) { + UnusedTracker.forProject(apiJars, _sourceSetsClassesDirs!!.files, toMinimize) + } else { + UnusedTracker.forProject(project.files(), project.files(), project.files()) + } + return ShadowCopyAction( + archiveFile.get().asFile, + internalCompressor, + documentationRegistry, + metadataCharset, + _transformers, + _relocators, + rootPatternSet, + stats, + isPreserveFileTimestamps, + minimizeJar, + unusedTracker, + ) + } + + @TaskAction + override fun copy() { + if (enableRelocation) { + configureRelocation(this, relocationPrefix) + } + from(includedDependencies) + super.copy() + logger.info(stats.toString()) + } + + override fun dependencies(action: Action?): ShadowJar = apply { + action?.execute(dependencyFilter) + } + + override fun transform(clazz: Class): ShadowJar { + return transform(clazz, null) + } + + override fun transform(clazz: Class, action: Action?): ShadowJar = apply { + val transformer = clazz.getDeclaredConstructor().newInstance() + addTransform(transformer, action) + } + + override fun transform(transformer: Transformer): ShadowJar = apply { + addTransform(transformer, null) + } + + override fun mergeServiceFiles(): ShadowJar = runOrThrow { + transform(ServiceFileTransformer::class.java) + } + + override fun mergeServiceFiles(rootPath: String): ShadowJar = runOrThrow { + transform(ServiceFileTransformer::class.java) { it.setPath(rootPath) } + } + + override fun mergeServiceFiles(action: Action?): ShadowJar = runOrThrow { + transform(ServiceFileTransformer::class.java, action) + } + + override fun mergeGroovyExtensionModules(): ShadowJar = runOrThrow { + transform(GroovyExtensionModuleTransformer::class.java) + } + + override fun append(resourcePath: String): ShadowJar = runOrThrow { + transform(AppendingTransformer::class.java) { it.resource = resourcePath } + } + + override fun relocate(pattern: String, destination: String): ShadowJar { + return relocate(pattern, destination, null) + } + + override fun relocate( + pattern: String, + destination: String, + action: Action?, + ): ShadowJar = apply { + val relocator = SimpleRelocator(pattern, destination, mutableListOf(), mutableListOf()) + addRelocator(relocator, action) + } + + override fun relocate(relocator: Relocator): ShadowJar = apply { + addRelocator(relocator, null) + } + + override fun relocate(clazz: Class): ShadowJar { + return relocate(clazz, null) + } + + override fun relocate(clazz: Class, action: Action?): ShadowJar = apply { + val relocator = clazz.getDeclaredConstructor().newInstance() + addRelocator(relocator, action) + } + + private fun isCacheableTransform(clazz: Class): Boolean { + return clazz.isAnnotationPresent(CacheableTransformer::class.java) + } + + private fun addTransform(transformer: T, action: Action?) { + action?.execute(transformer) + _transformers.add(transformer) + } + + private fun addRelocator(relocator: R, configure: Action?) { + configure?.execute(relocator) + _relocators.add(relocator) + } + + private fun isCacheableRelocator(clazz: Class): Boolean { + return clazz.isAnnotationPresent(CacheableRelocator::class.java) + } + + private fun configureRelocation(target: ShadowJar, prefix: String) { + target._configurations + .asSequence() + .flatMap { it.files } + .flatMap { JarFile(it).entries().asSequence() } + .filter { it.name.endsWith(".class") && it.name != "module-info.class" } + .forEach { + val pkg = it.name.substring(0, it.name.lastIndexOf('/')).replace('/', '.') + target.relocate(pkg, "$prefix.$pkg") + } + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt new file mode 100644 index 000000000..315547ef0 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt @@ -0,0 +1,26 @@ + + +package com.github.jengelman.gradle.plugins.shadow.transformers + +import org.gradle.api.file.FileTreeElement + +/** + * Prevents duplicate copies of the license + * + * Modified from `org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer.java` + * + * @author John Engelman + */ +public class ApacheLicenseResourceTransformer : Transformer by NoOpTransformer { + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return LICENSE_PATH.equals(path, ignoreCase = true) || + LICENSE_TXT_PATH.regionMatches(0, path, 0, LICENSE_TXT_PATH.length, ignoreCase = true) + } + + private companion object { + const val LICENSE_PATH = "META-INF/LICENSE" + const val LICENSE_TXT_PATH = "META-INF/LICENSE.txt" + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt new file mode 100644 index 000000000..33b58481f --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -0,0 +1,180 @@ + + +package com.github.jengelman.gradle.plugins.shadow.transformers + +import java.io.PrintWriter +import java.nio.charset.Charset +import java.text.SimpleDateFormat +import java.util.Date +import java.util.TreeSet +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional + +/** + * Merges `META-INF/NOTICE.TXT` files. + * + * Modified from `org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer.java`. + */ +public class ApacheNoticeResourceTransformer : Transformer { + + private val entries = LinkedHashSet() + private val organizationEntries = LinkedHashMap>() + + @Input + public var projectName: String = "" + + @Input + public var addHeader: Boolean = true + + @Input + public var preamble1: String = """ + // ------------------------------------------------------------------ + // NOTICE file corresponding to the section 4d of The Apache License, + // Version 2.0, in this case for + """.trimIndent() + + @Input + public var preamble2: String = "\n// ------------------------------------------------------------------\n" + + @Input + public var preamble3: String = "This product includes software developed at\n" + + @Input + public var organizationName: String = "The Apache Software Foundation" + + @Input + public var organizationURL: String = "https://www.apache.org/" + + @Input + public var inceptionYear: String = "2006" + + @Optional + @Input + public var copyright: String? = null + + /** + * The file encoding of the `NOTICE` file. + */ + @Optional + @Input + public var encoding: Charset = Charsets.UTF_8 + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return NOTICE_PATH.equals(path, ignoreCase = true) || NOTICE_TXT_PATH.equals(path, ignoreCase = true) + } + + override fun transform(context: TransformerContext) { + if (entries.isEmpty()) { + val year = SimpleDateFormat("yyyy").format(Date()) + val displayYear = if (inceptionYear != year) "$inceptionYear-$year" else year + + // add headers + if (addHeader) { + entries.add("$preamble1$projectName$preamble2") + } else { + entries.add("") + } + // fake second entry, we'll look for a real one later + entries.add("$projectName\nCopyright $displayYear $organizationName\n") + entries.add("$preamble3$organizationName ($organizationURL).\n") + } + + val reader = context.inputStream.bufferedReader(encoding) + + var line = reader.readLine() + val sb = StringBuffer() + var currentOrg: MutableSet? = null + var lineCount = 0 + while (line != null) { + val trimmedLine = line.trim() + + if (!trimmedLine.startsWith("//")) { + if (trimmedLine.isNotEmpty()) { + if (trimmedLine.startsWith("- ")) { + // resource-bundle 1.3 mode + if (lineCount == 1 && sb.toString().contains("This product includes/uses software(s) developed by")) { + currentOrg = organizationEntries.getOrPut(sb.toString().trim()) { TreeSet() } + sb.setLength(0) + } else if (sb.isNotEmpty() && currentOrg != null) { + currentOrg.add(sb.toString()) + sb.setLength(0) + } + } + sb.append(line).append("\n") + lineCount++ + } else { + val ent = sb.toString() + if (ent.startsWith(projectName) && ent.contains("Copyright ")) { + copyright = ent + } + if (currentOrg == null) { + entries.add(ent) + } else { + currentOrg.add(ent) + } + sb.setLength(0) + lineCount = 0 + currentOrg = null + } + } + + line = reader.readLine() + } + if (sb.isNotEmpty()) { + if (currentOrg == null) { + entries.add(sb.toString()) + } else { + currentOrg.add(sb.toString()) + } + } + } + + override fun hasTransformedResource(): Boolean = true + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val zipEntry = ZipEntry(NOTICE_PATH) + zipEntry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, zipEntry.time) + os.putNextEntry(zipEntry) + + val writer = PrintWriter(os.writer(encoding)) + + var count = 0 + for (line in entries) { + ++count + if (line == copyright && count != 2) { + continue + } + + if (count == 2 && copyright != null) { + writer.print(copyright) + writer.print('\n') + } else { + writer.print(line) + writer.print('\n') + } + if (count == 3) { + // do org stuff + for ((key, value) in organizationEntries) { + writer.print(key) + writer.print('\n') + for (l in value) { + writer.print(l) + } + writer.print('\n') + } + } + } + + writer.flush() + entries.clear() + } + + private companion object { + const val NOTICE_PATH = "META-INF/NOTICE" + const val NOTICE_TXT_PATH = "META-INF/NOTICE.txt" + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt new file mode 100644 index 000000000..a4d7d3243 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -0,0 +1,63 @@ + + +package com.github.jengelman.gradle.plugins.shadow.transformers + +import java.io.ByteArrayOutputStream +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional + +/** + * A resource processor that appends content for a resource, separated by a newline. + * + * Modified from `org.apache.maven.plugins.shade.resource.AppendingTransformer.java` + * + * @author John Engelman + */ +@CacheableTransformer +public class AppendingTransformer : Transformer { + + @Optional + @Input + public var resource: String? = null + + /** + * Defer initialization, see https://github.com/GradleUp/shadow/issues/763 + */ + private var data: ByteArrayOutputStream? = null + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return resource?.equals(path, ignoreCase = true) ?: false + } + + override fun transform(context: TransformerContext) { + if (data == null) { + data = ByteArrayOutputStream() + } + + context.inputStream.copyTo(data!!) + data!!.write('\n'.code) + + context.inputStream.close() + } + + override fun hasTransformedResource(): Boolean { + return (data?.size() ?: 0) > 0 + } + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + if (data == null) { + data = ByteArrayOutputStream() + } + + val entry = ZipEntry(resource) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + + data!!.toByteArray().inputStream().copyTo(os) + data!!.reset() + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt new file mode 100644 index 000000000..a240b27e9 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt @@ -0,0 +1,10 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +/** + * Marks that a given instance of [Transformer] is compatible with the Gradle build cache. + * In other words, it has its appropriate inputs annotated so that Gradle can consider them when + * determining the cache key. + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.CLASS) +public annotation class CacheableTransformer diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt new file mode 100644 index 000000000..330f3df7d --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -0,0 +1,145 @@ + + +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext +import java.io.BufferedInputStream +import java.io.ByteArrayOutputStream +import java.io.IOException +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.codehaus.plexus.util.xml.XmlStreamReader +import org.codehaus.plexus.util.xml.XmlStreamWriter +import org.codehaus.plexus.util.xml.Xpp3Dom +import org.codehaus.plexus.util.xml.Xpp3DomBuilder +import org.codehaus.plexus.util.xml.Xpp3DomWriter +import org.gradle.api.file.FileTreeElement + +/** + * A resource processor that aggregates plexus components.xml files. + * + * Modified from `org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer.java`. + * + * @author John Engelman + */ +public class ComponentsXmlResourceTransformer : Transformer { + private val components: MutableMap = LinkedHashMap() + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return COMPONENTS_XML_PATH == path + } + + override fun transform(context: TransformerContext) { + val newDom: Xpp3Dom = try { + val bis = object : BufferedInputStream(context.inputStream) { + @Throws(IOException::class) + override fun close() { + // leave ZIP open + } + } + Xpp3DomBuilder.build(XmlStreamReader(bis)) + } catch (e: Exception) { + throw IOException("Error parsing components.xml in ${context.inputStream}", e) + } + + // Only try to merge in components if there are some elements in the component-set + if (newDom.getChild("components") == null) return + + val children = newDom.getChild("components").getChildren("component") + + for (component in children) { + var role = getValue(component, "role") + role = getRelocatedClass(role, context) + setValue(component, "role", role) + + val roleHint = getValue(component, "role-hint") + + var impl = getValue(component, "implementation") + impl = getRelocatedClass(impl, context) + setValue(component, "implementation", impl) + + val key = "$role:$roleHint" + if (components.containsKey(key)) { + // TODO: use the tools in Plexus to merge these properly. For now, I just need an all-or-nothing + // configuration carry over + + val dom = components[key] + if (dom?.getChild("configuration") != null) { + component.addChild(dom.getChild("configuration")) + } + } + + val requirements = component.getChild("requirements") + if (requirements != null && requirements.childCount > 0) { + for (r in requirements.childCount - 1 downTo 0) { + val requirement = requirements.getChild(r) + + var requiredRole = getValue(requirement, "role") + requiredRole = getRelocatedClass(requiredRole, context) + setValue(requirement, "role", requiredRole) + } + } + + components[key] = component + } + } + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val data = getTransformedResource() + val entry = ZipEntry(COMPONENTS_XML_PATH) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + data.inputStream().copyTo(os) + components.clear() + } + + override fun hasTransformedResource(): Boolean = components.isNotEmpty() + + @Throws(IOException::class) + private fun getTransformedResource(): ByteArray { + val baos = ByteArrayOutputStream(1024 * 4) + XmlStreamWriter(baos).use { writer -> + val dom = Xpp3Dom("component-set") + val componentDom = Xpp3Dom("components") + dom.addChild(componentDom) + + for (component in components.values) { + componentDom.addChild(component) + } + + Xpp3DomWriter.write(writer, dom) + } + return baos.toByteArray() + } + + private fun getRelocatedClass(className: String?, context: TransformerContext): String { + val relocators = context.relocators + val stats = context.stats + if (!className.isNullOrEmpty()) { + for (relocator in relocators) { + if (relocator.canRelocateClass(className)) { + val relocateClassContext = RelocateClassContext(className, stats) + return relocator.relocateClass(relocateClassContext) + } + } + } + return className.orEmpty() + } + + private fun getValue(dom: Xpp3Dom, element: String): String { + val child = dom.getChild(element) + return child?.value.orEmpty() + } + + private fun setValue(dom: Xpp3Dom, element: String, value: String) { + val child = dom.getChild(element) + if (child != null && value.isNotEmpty()) { + child.value = value + } + } + + public companion object { + public const val COMPONENTS_XML_PATH: String = "META-INF/plexus/components.xml" + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt new file mode 100644 index 000000000..85dc6b942 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt @@ -0,0 +1,27 @@ + + +package com.github.jengelman.gradle.plugins.shadow.transformers + +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional + +/** + * A resource processor that prevents the inclusion of an arbitrary + * resource into the shaded JAR. + * + * Modified from org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer.java + * + * @author John Engelman + */ +public class DontIncludeResourceTransformer : Transformer by NoOpTransformer { + + @Optional + @Input + public var resource: String? = null + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return !resource.isNullOrEmpty() && path.endsWith(resource!!) + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt new file mode 100644 index 000000000..02f80fe14 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt @@ -0,0 +1,92 @@ + +package com.github.jengelman.gradle.plugins.shadow.transformers + +import java.io.ByteArrayOutputStream +import java.io.InputStream +import java.util.Properties +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement + +@CacheableTransformer +public class GroovyExtensionModuleTransformer : Transformer { + + private val module = Properties() + + /** + * default to Groovy 2.4 or earlier + */ + private var legacy = true + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return when (path) { + GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH -> { + // Groovy 2.5+ + legacy = false + true + } + GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH -> true + else -> false + } + } + + override fun transform(context: TransformerContext) { + val props = Properties() + props.load(context.inputStream) + props.forEach { key, value -> + when (key as String) { + MODULE_NAME_KEY -> handle(key, value as String) { + module.setProperty(key, MERGED_MODULE_NAME) + } + MODULE_VERSION_KEY -> handle(key, value as String) { + module.setProperty(key, MERGED_MODULE_VERSION) + } + EXTENSION_CLASSES_KEY, STATIC_EXTENSION_CLASSES_KEY -> handle(key, value as String) { existingValue -> + val newValue = "$existingValue,$value" + module.setProperty(key, newValue) + } + } + } + } + + private fun handle(key: String, value: String, mergeValue: (String) -> Unit) { + val existingValue = module.getProperty(key) + if (existingValue != null) { + mergeValue(existingValue) + } else { + module.setProperty(key, value) + } + } + + override fun hasTransformedResource(): Boolean = module.isNotEmpty() + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val path = if (legacy) GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH else GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH + val entry = ZipEntry(path).apply { + time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, time) + } + os.putNextEntry(entry) + module.toInputStream().copyTo(os) + os.closeEntry() + } + + private fun Properties.toInputStream(): InputStream { + val baos = ByteArrayOutputStream() + this.store(baos, null) + return baos.toByteArray().inputStream() + } + + private companion object { + const val GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH = + "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" + const val GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH = + "META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule" + const val MODULE_NAME_KEY = "moduleName" + const val MODULE_VERSION_KEY = "moduleVersion" + const val EXTENSION_CLASSES_KEY = "extensionClasses" + const val STATIC_EXTENSION_CLASSES_KEY = "staticExtensionClasses" + const val MERGED_MODULE_NAME = "MergedByShadowJar" + const val MERGED_MODULE_VERSION = "1.0.0" + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt new file mode 100644 index 000000000..925300a30 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt @@ -0,0 +1,41 @@ + + +package com.github.jengelman.gradle.plugins.shadow.transformers + +import java.io.File +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity + +/** + * A resource processor that allows the addition of an arbitrary file + * content into the shaded JAR. + * + * Modified from org.apache.maven.plugins.shade.resource.IncludeResourceTransformer.java + * + * @author John Engelman + */ +public class IncludeResourceTransformer : Transformer by NoOpTransformer { + + @InputFile + @PathSensitive(PathSensitivity.NONE) + public lateinit var file: File + + @Input + public lateinit var resource: String + + override fun hasTransformedResource(): Boolean { + return this::file.isInitialized && file.exists() + } + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val entry = ZipEntry(resource) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + + file.inputStream().copyTo(os) + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt new file mode 100644 index 000000000..76a839c97 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -0,0 +1,87 @@ + + +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import java.io.File +import java.io.FileOutputStream +import java.net.URL +import java.util.Collections +import java.util.Enumeration +import org.apache.commons.io.output.CloseShieldOutputStream +import org.apache.logging.log4j.core.config.plugins.processor.PluginCache +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement + +@CacheableTransformer +public class Log4j2PluginsCacheFileTransformer : Transformer { + + private val temporaryFiles = mutableListOf() + private val relocators = mutableListOf() + private var stats: ShadowStats? = null + + override fun canTransformResource(element: FileTreeElement): Boolean { + return PLUGIN_CACHE_FILE == element.name + } + + override fun transform(context: TransformerContext) { + val inputStream = context.inputStream + val temporaryFile = File.createTempFile("Log4j2Plugins", ".dat") + temporaryFile.deleteOnExit() + temporaryFiles.add(temporaryFile) + FileOutputStream(temporaryFile).use { fos -> + inputStream.copyTo(fos) + } + relocators.addAll(context.relocators) + if (stats == null) { + stats = context.stats + } + } + + override fun hasTransformedResource(): Boolean { + val hasTransformedMultipleFiles = temporaryFiles.size > 1 + val hasAtLeastOneFileAndRelocator = temporaryFiles.isNotEmpty() && relocators.isNotEmpty() + return hasTransformedMultipleFiles || hasAtLeastOneFileAndRelocator + } + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val pluginCache = PluginCache() + pluginCache.loadCacheFiles(urlEnumeration) + relocatePlugins(pluginCache) + val entry = ZipEntry(PLUGIN_CACHE_FILE).apply { + time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, time) + } + os.putNextEntry(entry) + pluginCache.writeCache(CloseShieldOutputStream.wrap(os)) + temporaryFiles.clear() + } + + private val urlEnumeration: Enumeration + get() { + val urls = temporaryFiles.map { it.toURI().toURL() } + return Collections.enumeration(urls) + } + + private fun relocatePlugins(pluginCache: PluginCache) { + for (currentMap in pluginCache.allCategories.values) { + for (currentPluginEntry in currentMap.values) { + val className = currentPluginEntry.className + val relocateClassContext = RelocateClassContext(className, stats!!) + for (currentRelocator in relocators) { + if (currentRelocator.canRelocateClass(className)) { + val relocatedClassName = currentRelocator.relocateClass(relocateClassContext) + currentPluginEntry.className = relocatedClassName + break + } + } + } + } + } + + private companion object { + const val PLUGIN_CACHE_FILE = "org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat" + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt new file mode 100644 index 000000000..6785d98e0 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -0,0 +1,62 @@ + + +package com.github.jengelman.gradle.plugins.shadow.transformers + +import java.io.IOException +import java.nio.charset.StandardCharsets.UTF_8 +import java.util.jar.JarFile.MANIFEST_NAME +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.slf4j.LoggerFactory + +@CacheableTransformer +public class ManifestAppenderTransformer : Transformer { + private val logger = LoggerFactory.getLogger(this::class.java) + + private val _attributes = mutableListOf>>() + private var manifestContents = ByteArray(0) + + @Input + public val attributes: List>> = _attributes + + public fun append(name: String, value: Comparable<*>): ManifestAppenderTransformer = apply { + _attributes.add(name to value) + } + + override fun canTransformResource(element: FileTreeElement): Boolean { + return MANIFEST_NAME.equals(element.relativePath.pathString, ignoreCase = true) + } + + override fun transform(context: TransformerContext) { + if (manifestContents.isEmpty()) { + try { + manifestContents = context.inputStream.readBytes() + } catch (e: IOException) { + logger.warn("Failed to read MANIFEST.MF", e) + } + } + } + + override fun hasTransformedResource(): Boolean = attributes.isNotEmpty() + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val entry = ZipEntry(MANIFEST_NAME).apply { + time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, time) + } + os.putNextEntry(entry) + os.write(manifestContents) + + if (_attributes.isNotEmpty()) { + _attributes.forEach { (name, value) -> + os.write(name.toByteArray(UTF_8)) + os.write(": ".toByteArray(UTF_8)) + os.write(value.toString().toByteArray(UTF_8)) + os.write("\r\n".toByteArray(UTF_8)) + } + os.write("\r\n".toByteArray(UTF_8)) + _attributes.clear() + } + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt new file mode 100644 index 000000000..805decf3b --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -0,0 +1,77 @@ + + +package com.github.jengelman.gradle.plugins.shadow.transformers + +import java.io.IOException +import java.util.jar.Attributes +import java.util.jar.JarFile +import java.util.jar.Manifest +import org.apache.log4j.LogManager +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional + +@CacheableTransformer +public class ManifestResourceTransformer : Transformer { + private val logger = LogManager.getLogger(this::class.java) + + @Optional + @Input + public var mainClass: String? = null + + @Optional + @Input + public var manifestEntries: MutableMap? = null + + private var manifestDiscovered = false + private var manifest: Manifest? = null + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return JarFile.MANIFEST_NAME.equals(path, ignoreCase = true) + } + + override fun transform(context: TransformerContext) { + if (!manifestDiscovered) { + try { + manifest = Manifest(context.inputStream) + manifestDiscovered = true + } catch (e: IOException) { + logger.warn("Failed to read MANIFEST.MF", e) + } + } + } + + override fun hasTransformedResource(): Boolean = true + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + if (manifest == null) { + manifest = Manifest() + } + + val attributes = manifest!!.mainAttributes + + mainClass?.let { + attributes.put(Attributes.Name.MAIN_CLASS, it) + } + + manifestEntries?.forEach { (key, value) -> + attributes[Attributes.Name(key)] = value + } + + val entry = ZipEntry(JarFile.MANIFEST_NAME).apply { + time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, time) + } + os.putNextEntry(entry) + manifest!!.write(os) + } + + public fun attributes(attributes: Map): ManifestResourceTransformer = apply { + if (manifestEntries == null) { + manifestEntries = mutableMapOf() + } + manifestEntries!!.putAll(attributes.mapValues { Attributes().apply { putValue(it.key, it.value.toString()) } }) + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/NoOpTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/NoOpTransformer.kt new file mode 100644 index 000000000..4da2796c0 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/NoOpTransformer.kt @@ -0,0 +1,11 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement + +public object NoOpTransformer : Transformer { + public override fun canTransformResource(element: FileTreeElement): Boolean = false + public override fun transform(context: TransformerContext): Unit = Unit + public override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean): Unit = Unit + public override fun hasTransformedResource(): Boolean = false +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt new file mode 100644 index 000000000..4f63387bf --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -0,0 +1,227 @@ + + +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.internal.CleanProperties +import java.io.ByteArrayOutputStream +import java.io.InputStream +import java.io.InputStreamReader +import java.nio.charset.Charset +import java.util.Properties +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal + +/** + * Resources transformer that merges Properties files. + * + *

The default merge strategy discards duplicate values coming from additional + * resources. This behavior can be changed by setting a value for the mergeStrategy + * property, such as 'first' (default), 'latest' or 'append'. If the merge strategy is + * 'latest' then the last value of a matching property entry will be used. If the + * merge strategy is 'append' then the property values will be combined, using a + * merge separator (default value is ','). The merge separator can be changed by + * setting a value for the mergeSeparator property.

+ * + * Say there are two properties files A and B with the + * following entries: + * + * A + *
    + *
  • key1 = value1
  • + *
  • key2 = value2
  • + *
+ * + * B + *
    + *
  • key2 = balue2
  • + *
  • key3 = value3
  • + *
+ * + * With mergeStrategy = first you get + * + * C + *
    + *
  • key1 = value1
  • + *
  • key2 = value2
  • + *
  • key3 = value3
  • + *
+ * + * With mergeStrategy = latest you get + * + * C + *
    + *
  • key1 = value1
  • + *
  • key2 = balue2
  • + *
  • key3 = value3
  • + *
+ * + * With mergeStrategy = append and mergeSparator = ; you get + * + * C + *
    + *
  • key1 = value1
  • + *
  • key2 = value2;balue2
  • + *
  • key3 = value3
  • + *
+ * + *

There are three additional properties that can be set: paths, mappings, + * and keyTransformer. + * The first contains a list of strings or regexes that will be used to determine if + * a path should be transformed or not. The merge strategy and merge separator are + * taken from the global settings.

+ * + *

The mappings property allows you to define merge strategy and separator per + * path

. If either paths or mappings is defined then no other path + * entries will be merged. mappings has precedence over paths if both + * are defined.

+ * + *

If you need to transform keys in properties files, e.g. because they contain class + * names about to be relocated, you can set the keyTransformer property to a + * closure that receives the original key and returns the key name to be used.

+ * + *

Example:

+ *
+ * import org.codehaus.griffon.gradle.shadow.transformers.*
+ * tasks.named('shadowJar', ShadowJar) {
+ *     transform(PropertiesFileTransformer) {
+ *         paths = [
+ *             'META-INF/editors/java.beans.PropertyEditor'
+ *         ]
+ *         keyTransformer = { key ->
+ *             key.replaceAll('^(orig\.package\..*)$', 'new.prefix.$1')
+ *         }
+ *     }
+ * }
+ * 
+ */ +public class PropertiesFileTransformer : Transformer { + private val propertiesEntries = mutableMapOf() + + @Input + public var paths: List = listOf() + + @Input + public var mappings: Map> = mapOf() + + /** + * latest, append + */ + @Input + public var mergeStrategy: String = "first" + + @Input + public var mergeSeparator: String = "," + + @Input + public var charset: Charset = Charsets.ISO_8859_1 + + @Internal + public var keyTransformer: (String) -> String = defaultKeyTransformer + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + if (mappings.containsKey(path)) return true + for (key in mappings.keys) { + if (path.matches(key.toRegex())) return true + } + + if (path in paths) return true + for (p in paths) { + if (path.matches(p.toRegex())) return true + } + + return mappings.isEmpty() && paths.isEmpty() && path.endsWith(PROPERTIES_SUFFIX) + } + + override fun transform(context: TransformerContext) { + val props = propertiesEntries[context.path] + val incoming = loadAndTransformKeys(context.inputStream) + if (props == null) { + propertiesEntries[context.path] = incoming + } else { + incoming.forEach { (key, value) -> + if (props.containsKey(key)) { + when (mergeStrategyFor(context.path).lowercase()) { + "latest" -> props[key] = value + "append" -> props[key] = props.getProperty(key.toString()) + mergeSeparatorFor(context.path) + value + "first" -> Unit // continue + else -> Unit // continue + } + } else { + props[key] = value + } + } + } + } + + private fun loadAndTransformKeys(inputStream: InputStream?): CleanProperties { + val props = CleanProperties() + inputStream?.let { + props.load(it.reader(charset)) + } + return transformKeys(props) as CleanProperties + } + + private fun transformKeys(properties: Properties): Properties { + if (keyTransformer == defaultKeyTransformer) return properties + val result = CleanProperties() + properties.forEach { (key, value) -> + result[keyTransformer(key as String)] = value + } + return result + } + + private fun mergeStrategyFor(path: String): String { + mappings[path]?.let { + return it["mergeStrategy"] ?: mergeStrategy + } + for (key in mappings.keys) { + if (path.matches(key.toRegex())) { + return mappings[key]?.get("mergeStrategy") ?: mergeStrategy + } + } + return mergeStrategy + } + + private fun mergeSeparatorFor(path: String): String { + mappings[path]?.let { + return it["mergeSeparator"] ?: mergeSeparator + } + for (key in mappings.keys) { + if (path.matches(Regex(key))) { + return mappings[key]?.get("mergeSeparator") ?: mergeSeparator + } + } + return mergeSeparator + } + + override fun hasTransformedResource(): Boolean = propertiesEntries.isNotEmpty() + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val zipWriter = os.writer(charset) + propertiesEntries.forEach { (path, props) -> + val entry = ZipEntry(path) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + readerFor(props, charset).copyTo(zipWriter) + zipWriter.flush() + os.closeEntry() + } + } + + private fun readerFor(props: Properties, charset: Charset): InputStreamReader { + val baos = ByteArrayOutputStream() + baos.writer(charset).use { writer -> + props.store(writer, "") + } + return baos.toByteArray().inputStream().reader(charset) + } + + private companion object { + const val PROPERTIES_SUFFIX = ".properties" + val defaultKeyTransformer: (String) -> String = { it } + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt new file mode 100644 index 000000000..d360a785d --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -0,0 +1,88 @@ + +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.IOException +import java.io.InputStream +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.util.PatternFilterable +import org.gradle.api.tasks.util.PatternSet + +@CacheableTransformer +public class ServiceFileTransformer private constructor( + private val patternSet: PatternSet, +) : Transformer, + PatternFilterable by patternSet { + private val serviceEntries = mutableMapOf() + + public constructor() : this( + PatternSet() + .include(SERVICES_PATTERN) + .exclude(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN), + ) + + public fun setPath(path: String) { + patternSet.setIncludes(listOf("$path/**")) + } + + override fun canTransformResource(element: FileTreeElement): Boolean { + val target = if (element is ShadowCopyAction.ArchiveFileTreeElement) element.asFileTreeElement() else element + return patternSet.asSpec.isSatisfiedBy(target) + } + + override fun transform(context: TransformerContext) { + val lines = context.inputStream.bufferedReader().readLines().toMutableList() + var targetPath = context.path + context.relocators.forEach { rel -> + if (rel.canRelocateClass(File(targetPath).name)) { + targetPath = rel.relocateClass(RelocateClassContext(targetPath, context.stats)) + } + lines.forEachIndexed { i, line -> + if (rel.canRelocateClass(line)) { + val lineContext = RelocateClassContext(line, context.stats) + lines[i] = rel.relocateClass(lineContext) + } + } + } + lines.forEach { line -> + serviceEntries.getOrPut(targetPath) { ServiceStream() }.append(line.toByteArray().inputStream()) + } + } + + override fun hasTransformedResource(): Boolean = serviceEntries.isNotEmpty() + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + serviceEntries.forEach { (path, stream) -> + val entry = ZipEntry(path) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + stream.toInputStream().copyTo(os) + os.closeEntry() + } + } + + public class ServiceStream : ByteArrayOutputStream(1024) { + @Throws(IOException::class) + public fun append(inputStream: InputStream) { + if (count > 0 && buf[count - 1] != '\n'.code.toByte() && buf[count - 1] != '\r'.code.toByte()) { + val newline = "\n".toByteArray() + write(newline, 0, newline.size) + } + inputStream.copyTo(this) + } + + public fun toInputStream(): InputStream = ByteArrayInputStream(buf, 0, count) + } + + private companion object { + const val SERVICES_PATTERN = "META-INF/services/**" + const val GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN = + "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt new file mode 100644 index 000000000..7341ac0ca --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt @@ -0,0 +1,27 @@ + +package com.github.jengelman.gradle.plugins.shadow.transformers + +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.Named +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Internal + +/** + * Modified from org.apache.maven.plugins.shade.resource.ResourceTransformer.java + * + * @author Jason van Zyl + * @author Charlie Knudsen + * @author John Engelman + */ +public interface Transformer : Named { + public fun canTransformResource(element: FileTreeElement): Boolean + + public fun transform(context: TransformerContext) + + public fun hasTransformedResource(): Boolean + + public fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) + + @Internal + override fun getName(): String = this::class.java.simpleName +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt new file mode 100644 index 000000000..b5a8cf86c --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt @@ -0,0 +1,44 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction +import java.io.InputStream + +public data class TransformerContext( + val path: String, + val inputStream: InputStream, + val relocators: List, + val stats: ShadowStats, +) { + public class Builder { + private var path: String? = null + private var inputStream: InputStream? = null + private var relocators: List? = null + private var stats: ShadowStats? = null + + public fun path(path: String): Builder = apply { this.path = path } + public fun inputStream(inputStream: InputStream): Builder = apply { this.inputStream = inputStream } + public fun relocators(relocators: List): Builder = apply { this.relocators = relocators } + public fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } + + public fun build(): TransformerContext { + return TransformerContext( + requireNotNull(path), + requireNotNull(inputStream), + requireNotNull(relocators), + requireNotNull(stats), + ) + } + } + + public companion object { + @JvmStatic + public fun getEntryTimestamp(preserveFileTimestamps: Boolean, entryTime: Long): Long { + return if (preserveFileTimestamps) entryTime else ShadowCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES + } + + @JvmStatic + public fun builder(): Builder = Builder() + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt new file mode 100644 index 000000000..9879789e2 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -0,0 +1,80 @@ + + +package com.github.jengelman.gradle.plugins.shadow.transformers + +import java.io.StringReader +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional +import org.jdom2.Document +import org.jdom2.JDOMException +import org.jdom2.input.SAXBuilder +import org.jdom2.input.sax.XMLReaders +import org.jdom2.output.Format +import org.jdom2.output.XMLOutputter +import org.xml.sax.EntityResolver +import org.xml.sax.InputSource + +@CacheableTransformer +public class XmlAppendingTransformer : Transformer { + + @Input + public var ignoreDtd: Boolean = true + + @Optional + @Input + public var resource: String? = null + + private var doc: Document? = null + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return resource?.equals(path, ignoreCase = true) == true + } + + override fun transform(context: TransformerContext) { + val r: Document + try { + val builder = SAXBuilder(XMLReaders.NONVALIDATING).apply { + expandEntities = false + if (ignoreDtd) { + entityResolver = EntityResolver { _, _ -> InputSource(StringReader("")) } + } + } + r = builder.build(context.inputStream) + } catch (e: JDOMException) { + throw RuntimeException("Error processing resource $resource: ${e.message}", e) + } + + if (doc == null) { + doc = r + } else { + val root = r.rootElement + + root.attributes.forEach { a -> + val mergedEl = doc!!.rootElement + val mergedAtt = mergedEl.getAttribute(a.name, a.namespace) + if (mergedAtt == null) { + mergedEl.setAttribute(a) + } + } + + root.children.forEach { n -> + doc!!.rootElement.addContent(n.clone()) + } + } + } + + override fun hasTransformedResource(): Boolean = doc != null + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val entry = ZipEntry(resource).apply { + time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, time) + } + os.putNextEntry(entry) + XMLOutputter(Format.getPrettyFormat()).output(doc, os) + doc = null + } +} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy index 2b42eb3da..1bd365422 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy @@ -29,7 +29,7 @@ import junit.framework.TestCase * @version $Id: SimpleRelocatorTest.java 1342979 2012-05-26 22:05:45Z bimargulies $ * * Modified from org.apache.maven.plugins.shade.relocation.SimpleRelocatorTest.java - * + * * Modifications * @author John Engelman */ @@ -94,7 +94,7 @@ class SimpleRelocatorTest extends TestCase { assertEquals(true, relocator.canRelocatePath("org/f")) // equal to path pattern assertEquals(true, relocator.canRelocatePath("/org/f")) // equal to path pattern with / } - + void testCanRelocatePathWithRegex() { SimpleRelocator relocator @@ -202,12 +202,12 @@ class SimpleRelocatorTest extends TestCase { relocator = new SimpleRelocator("^META-INF/org.foo.xml\$", "META-INF/hidden.org.foo.xml", null, null, true) assertEquals("META-INF/hidden.org.foo.xml", relocator.relocatePath(pathContext("META-INF/org.foo.xml"))) } - + protected RelocatePathContext pathContext(String path) { - return RelocatePathContext.builder().path(path).stats(stats).build() + return new RelocatePathContext(path, stats) } protected RelocateClassContext classContext(String className) { - return RelocateClassContext.builder().className(className).stats(stats).build() + return new RelocateClassContext(className, stats) } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy index 4bfc58d0f..c79b10b64 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy @@ -73,7 +73,7 @@ class ApacheNoticeResourceTransformerParameterTests extends TestCase { try { final ByteArrayInputStream noticeInputStream = new ByteArrayInputStream(noticeText.getBytes()) final List emptyList = Collections.emptyList() - subject.transform(TransformerContext.builder().path(NOTICE_RESOURCE).is(noticeInputStream).relocators(emptyList).stats(stats).build()) + subject.transform(TransformerContext.builder().path(NOTICE_RESOURCE).inputStream(noticeInputStream).relocators(emptyList).stats(stats).build()) } catch (NullPointerException ignored) { fail("Null pointer should not be thrown when no parameters are set.") diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy index ddb0876be..88379171f 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy @@ -53,14 +53,14 @@ class ComponentsXmlResourceTransformerTest extends TestCase { transformer.transform( TransformerContext.builder() .path("components-1.xml") - .is(getClass().getResourceAsStream("/components-1.xml")) + .inputStream(getClass().getResourceAsStream("/components-1.xml")) .relocators(Collections. emptyList()) .stats(stats) .build()) transformer.transform( TransformerContext.builder() .path("components-1.xml") - .is(getClass().getResourceAsStream("/components-2.xml")) + .inputStream(getClass().getResourceAsStream("/components-2.xml")) .relocators(Collections. emptyList()) .stats(stats) .build()) @@ -71,4 +71,4 @@ class ComponentsXmlResourceTransformerTest extends TestCase { // IOUtil.toString( transformer.getTransformedResource(), "UTF-8" ).replaceAll("\r\n", "\n") ) XMLAssert.assertXMLIdentical(diff, true) } -} \ No newline at end of file +} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy index fe030d0bd..59ee434d7 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy @@ -47,11 +47,11 @@ class TransformerSpecSupport extends Specification { } protected TransformerContext context(String path, Map input, String charset = 'ISO_8859_1') { - TransformerContext.builder().path(path).is(toInputStream(toProperties(input), charset)).relocators([]).stats(stats).build() + TransformerContext.builder().path(path).inputStream(toInputStream(toProperties(input), charset)).relocators([]).stats(stats).build() } protected TransformerContext context(String path, String input) { - TransformerContext.builder().path(path).is(toInputStream(input)).relocators([]).stats(stats).build() + TransformerContext.builder().path(path).inputStream(toInputStream(input)).relocators([]).stats(stats).build() } }