From bd1a39cc21c76fd0429a4b4736b69691210f5dfe Mon Sep 17 00:00:00 2001 From: pmupkin <399284508@qq.com> Date: Tue, 29 Oct 2024 15:51:26 +0800 Subject: [PATCH] gradle implementation for the Ark plugin (#1012) * gradle implementation for the Ark plugin --------- Co-authored-by: pmupkin <399284508@qq.com> Co-authored-by: gaosaroma (cherry picked from commit 0a41e920a2a851189d4fbf798cd1cfca2890c741) --- .gitignore | 6 + .../ark-plugin-gradle-plugin/README.md | 151 +++++++++ .../ark-plugin-gradle-plugin/build.gradle | 54 ++++ .../alipay/sofa/ark/boot/mojo/ArkPlugin.java | 35 +++ .../ark/boot/mojo/ArkPluginCopyAction.java | 211 +++++++++++++ .../ark/boot/mojo/ArkPluginExtension.java | 74 +++++ .../sofa/ark/boot/mojo/ArkPluginJarTask.java | 296 ++++++++++++++++++ .../alipay/sofa/ark/boot/mojo/BaseConfig.java | 61 ++++ .../ark/boot/mojo/ArkPluginExtensionTest.java | 64 ++++ 9 files changed, 952 insertions(+) create mode 100644 sofa-ark-parent/support/ark-plugin-gradle-plugin/README.md create mode 100644 sofa-ark-parent/support/ark-plugin-gradle-plugin/build.gradle create mode 100644 sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPlugin.java create mode 100644 sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPluginCopyAction.java create mode 100644 sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPluginExtension.java create mode 100644 sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPluginJarTask.java create mode 100644 sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/BaseConfig.java create mode 100644 sofa-ark-parent/support/ark-plugin-gradle-plugin/src/test/java/com/alipay/sofa/ark/boot/mojo/ArkPluginExtensionTest.java diff --git a/.gitignore b/.gitignore index d7e75f7ef..9bb9e8a23 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,9 @@ sofa-ark-parent/support/ark-plugin-maven-plugin/null.ark.plugin.bak sofa-ark-parent/support/ark-plugin-maven-plugin/xxx.ark.plugin sofa-ark-parent/core/common/C/\temp dir\b\c/test.txt sofa-ark-parent/core/common/C:\\temp dir\\b\\c/test.txt + +.gradle/ +build/ +gradle/ +gradlew +gradlew.bat \ No newline at end of file diff --git a/sofa-ark-parent/support/ark-plugin-gradle-plugin/README.md b/sofa-ark-parent/support/ark-plugin-gradle-plugin/README.md new file mode 100644 index 000000000..353284706 --- /dev/null +++ b/sofa-ark-parent/support/ark-plugin-gradle-plugin/README.md @@ -0,0 +1,151 @@ +# Ark Plugin Gradle打包插件使用 +`sofa-ark-plugin-gradle-plugin`模块是Ark Plugin打包工具的Gradle版本实现,和Maven打包工具`sofa-ark-plugin-maven-plugin`有同样的功能。在后文,使用**Gradle插件**来指代`sofa-ark-plugin-gradle-plugin`。 + +本小节会对**Gradle插件**进行介绍,随后会展示如何使用**Gradle插件**打包Ark Plugin。 + +### 配置项 +**Gradle插件**提供了和Maven基本相同的配置项,在使用上略有不同,想要使用配置项,需要在打包的项目build.gradle使用arkPlugin: + +``` +arkPlugin{ + //具体配置项 +} +``` + +- activator使用 +``` +activator = 'sample.activator.SamplePluginActivator' +``` + +- excludes使用 +``` +excludes = ['com.fasterxml.jackson.module:jackson-module-parameter-names', 'org.example:common'] +``` + +- exported使用 +``` +exported { + packages = [ + 'com.alipay.sofa.ark.sample.common' + + ] + classes = [ + 'sample.facade.SamplePluginService' + ] + + resource = [ + 'META-INF/spring/bean.xml' + ] + +} +``` + + +### 引入 +引入方式可以分为两种: +1. 本地引入 +2. 远程引入(正在申请) + +本地引入的方式是将Gradle插件发布到本地的Maven仓库中,之后使用Gradle进行加载。 + +#### 将Gradle插件发布到本地仓库 +1. 在**Gradle插件**的build.gradle的plugin解开注释,如下所示: +``` +plugins { + id 'java' + id 'java-gradle-plugin' + +// 本地调试用,发布到maven + id 'maven-publish' +} +``` + +2. 配置publish + 在build.gradle中增加如下内容: +``` +publishing { + // 配置Plugin GAV + publications { + maven(MavenPublication) { + groupId = group + artifactId = 'plugin' + version = version + from components.java + } + } + // 配置仓库地址 + repositories { + maven { + url file('E:/repo') + } + + } +} +``` +点击在IDEA的右侧Gradle中的 Tasks > publishing > publish 将插件发布到本地仓库。 + +#### 在本地项目中引入 + +1. 在Gradle项目根目录的setting.gradle中设置pluginManagement + +``` + pluginManagement { + repositories { + // 指定maven仓库 + maven { + url "file:///E:/repo" + } + } +} +``` + +2. 在需要打包的项目中的build.gradle进行如下的配置 + +``` +buildscript { + repositories { + // ... + maven { + url "file:///E:/repo" + } + + } + dependencies { + classpath("sofa.ark.gradle:sofa-ark-gradle-plugin:1.1") + } +} + +plugins { + id 'sofa.ark.gradle.plugin' version "1.1" +} +``` + +3. 增加配置 + +在需要打包的项目中的build.gradle创建配置项: + +``` +arkPlugin{ + outputDirectory = layout.buildDirectory.dir("custom-output") + + activator = 'sample.activator.SamplePluginActivator' + excludes = ['com.fasterxml.jackson.module:jackson-module-parameter-names'] + + shades = [ + 'org.example:common:1.0' + ] + exported { + packages = [ + 'com.alipay.sofa.ark.sample.common' + + ] + classes = [ + 'sample.facade.SamplePluginService' + ] + + } +} + +``` + +使用Gradle刷新后,如果一切正常,会在IDEA右侧Gradle任务列表中出现arkPluginJar,具体如下: Tasks > build > arkPluginJar,点击arkPluginJa执行,会在指定的outputDirectory中输出Ark Plugin包。 \ No newline at end of file diff --git a/sofa-ark-parent/support/ark-plugin-gradle-plugin/build.gradle b/sofa-ark-parent/support/ark-plugin-gradle-plugin/build.gradle new file mode 100644 index 000000000..d31308bfe --- /dev/null +++ b/sofa-ark-parent/support/ark-plugin-gradle-plugin/build.gradle @@ -0,0 +1,54 @@ +plugins { + id 'java' + id 'java-gradle-plugin' + id 'maven-publish' +} + +ext { + arkPluginGradlePluginId = 'sofa-ark-plugin-gradle-plugin' +} + +group = 'com.alipay.sofa' +version = '1.0.0' +sourceCompatibility = '1.8' + +gradlePlugin { + plugins { + DependenciesPlugin{ + id = arkPluginGradlePluginId + implementationClass = 'com.alipay.sofa.ark.boot.mojo.ArkPlugin' + } + } +} + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + implementation 'org.apache.commons:commons-compress:1.26.0' + testImplementation 'junit:junit:4.13.1' + testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.8.2") +} + +publishing { + publications { + maven(MavenPublication) { + from components.java + groupId = project.group + artifactId = arkPluginGradlePluginId + version = project.version + } + } + // publish to local maven repository + repositories { + maven { + mavenLocal() + } + } +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPlugin.java b/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPlugin.java new file mode 100644 index 000000000..8f85a5418 --- /dev/null +++ b/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPlugin.java @@ -0,0 +1,35 @@ +/* + * 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.alipay.sofa.ark.boot.mojo; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; + +public class ArkPlugin implements Plugin { + + @Override + public void apply(Project project) { + project.getExtensions().create("arkPlugin", ArkPluginExtension.class, project); + project.getTasks().register("arkPluginJar", ArkPluginJarTask.class, + task -> { + task.setGroup("build"); + task.setDescription("Generates an Ark plugin JAR file"); + } + ); + } + +} diff --git a/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPluginCopyAction.java b/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPluginCopyAction.java new file mode 100644 index 000000000..a2c22a687 --- /dev/null +++ b/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPluginCopyAction.java @@ -0,0 +1,211 @@ +/* + * 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.alipay.sofa.ark.boot.mojo; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Set; +import java.util.zip.CRC32; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import org.apache.commons.compress.archivers.zip.UnixStat; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; +import org.gradle.api.internal.file.CopyActionProcessingStreamAction; +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; + +public class ArkPluginCopyAction implements CopyAction { + private final File jarFile; + + private final Set shadeFiles; + + public ArkPluginCopyAction(File jarFile, Set shadeFiles) { + this.jarFile = jarFile; + this.shadeFiles = shadeFiles; + } + + @Override + public WorkResult execute(CopyActionProcessingStream stream) { + try (ZipArchiveOutputStream zipStream = new ZipArchiveOutputStream(jarFile)) { + zipStream.setEncoding(String.valueOf(StandardCharsets.UTF_8)); + StreamAction action = new StreamAction(zipStream, shadeFiles); + stream.process(action); + return WorkResults.didWork(true); + } catch (IOException e) { + throw new RuntimeException("Failed to create JAR file", e); + } + } + + private static class StreamAction implements CopyActionProcessingStreamAction { + private final ZipArchiveOutputStream zipStream; + + private final Set shadeFiles; + StreamAction(ZipArchiveOutputStream zipStream, Set shadeFiles) { + this.zipStream = zipStream; + this.shadeFiles = shadeFiles; + } + + @Override + public void processFile(FileCopyDetailsInternal details) { + try { + if (details.isDirectory()) { + addDirectory(details); + } else if (shadeFiles.contains(details.getFile())) { + addShadeFileContents(details.getFile()); + } else { + addFile(details); + } + } catch (IOException e) { + throw new RuntimeException("Failed to add file to JAR: " + details.getPath(), e); + } + } + + private void addDirectory(FileCopyDetailsInternal details) throws IOException { + ZipArchiveEntry entry = createEntry(details); + zipStream.putArchiveEntry(entry); + zipStream.closeArchiveEntry(); + } + + private void addShadeFileContents(File shadeFile) throws IOException { + try (ZipFile zipFile = new ZipFile(shadeFile)) { + zipFile.stream() + .filter(this::shouldProcessEntry) + .forEach(entry -> processShadeEntry(zipFile, entry)); + } + } + + private void addFile(FileCopyDetailsInternal details) throws IOException { + + ZipArchiveEntry entry = createEntry(details); + + String path = details.getRelativePath().getPathString(); + if (path.startsWith("lib")) { + try (InputStream inputStream = details.open()) { + CrcAndSize crcAndSize = new CrcAndSize(inputStream); + crcAndSize.setUpStoredEntry(entry); + } catch (Exception e) { + throw new IOException("please check this jar file"); + } + } + + zipStream.putArchiveEntry(entry); + details.copyTo(zipStream); + zipStream.closeArchiveEntry(); + } + + private ZipArchiveEntry createEntry(FileCopyDetailsInternal details){ + String path = details.isDirectory() ? details.getRelativePath().getPathString() + '/' : details.getRelativePath().getPathString(); + ZipArchiveEntry entry = new ZipArchiveEntry(path); + entry.setTime(details.getLastModified()); + int unixMode = details.getMode() | (details.isDirectory() ? UnixStat.DIR_FLAG : UnixStat.FILE_FLAG); + entry.setUnixMode(unixMode); + return entry; + } + + private boolean shouldProcessEntry(ZipEntry entry) { + return !"META-INF/MANIFEST.MF".equals(entry.getName()); + } + + private void processShadeEntry(ZipFile zipFile, ZipEntry entry) { + try { + ZipArchiveEntry newEntry = createNewEntry(entry); + + if (entry.isDirectory()) { + addDirectoryEntry(newEntry); + } else { + addFileEntry(zipFile, entry, newEntry); + } + } catch (IOException e) { + throw new RuntimeException("Failed to process shade entry: " + entry.getName(), e); + } + } + + private ZipArchiveEntry createNewEntry(ZipEntry entry) { + ZipArchiveEntry newEntry = new ZipArchiveEntry(entry.getName()); + newEntry.setTime(entry.getTime()); + newEntry.setUnixMode(UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM); + return newEntry; + } + + private void addDirectoryEntry(ZipArchiveEntry entry) throws IOException { + zipStream.putArchiveEntry(entry); + zipStream.closeArchiveEntry(); + } + + private void addFileEntry(ZipFile zipFile, ZipEntry entry, ZipArchiveEntry newEntry) throws IOException { + zipStream.putArchiveEntry(newEntry); + try (InputStream inputStream = zipFile.getInputStream(entry)) { + copy(inputStream, zipStream); + } + zipStream.closeArchiveEntry(); + } + + private void copy(InputStream in, OutputStream out) throws IOException { + byte[] buffer = new byte[8192]; + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) { + out.write(buffer, 0, bytesRead); + } + } + } + + + /** + * Data holder for CRC and Size. + */ + private static class CrcAndSize { + + private static final int BUFFER_SIZE = 32 * 1024; + + private final CRC32 crc = new CRC32(); + + private long size; + + CrcAndSize(InputStream inputStream) throws IOException { + try { + load(inputStream); + } + finally { + inputStream.close(); + } + } + + private void load(InputStream inputStream) throws IOException { + byte[] buffer = new byte[BUFFER_SIZE]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + this.crc.update(buffer, 0, bytesRead); + this.size += bytesRead; + } + } + + void setUpStoredEntry(ZipArchiveEntry entry) { + entry.setSize(this.size); + entry.setCompressedSize(this.size); + entry.setCrc(this.crc.getValue()); + entry.setMethod(ZipEntry.STORED); + } + } + +} diff --git a/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPluginExtension.java b/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPluginExtension.java new file mode 100644 index 000000000..03fa503f3 --- /dev/null +++ b/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPluginExtension.java @@ -0,0 +1,74 @@ +/* + * 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.alipay.sofa.ark.boot.mojo; + +import org.gradle.api.Action; +import org.gradle.api.Project; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.SetProperty; + +abstract public class ArkPluginExtension { + + public ArkPluginExtension(Project project){ + getPriority().convention(project.provider(() -> "100")); + getOutputDirectory().convention(project.getLayout().getBuildDirectory().dir("libs")); + getPluginName().convention(project.getName()); + getDescription().convention(""); + getActivator().convention(""); + } + + abstract public SetProperty getShades(); + abstract public SetProperty getExcludeArtifactIds(); + abstract public SetProperty getExcludeGroupIds(); + abstract public SetProperty getExcludes(); + abstract public Property getActivator(); + abstract public Property getPriority(); + abstract public Property getPluginName(); + + abstract public Property getDescription(); + abstract public DirectoryProperty getOutputDirectory(); + abstract public Property getAttach(); + + private final ImportedConfig imported = new ImportedConfig(); + private final ExportedConfig exported = new ExportedConfig(); + + public ImportedConfig getImported() { + return imported; + } + + public void imported(Action action) { + action.execute(imported); + } + + public ExportedConfig getExported() { + return exported; + } + + public void exported(Action action) { + action.execute(exported); + } + + public static class ImportedConfig extends BaseConfig{ + + } + + public static class ExportedConfig extends BaseConfig{ + + } + +} diff --git a/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPluginJarTask.java b/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPluginJarTask.java new file mode 100644 index 000000000..27ab838ae --- /dev/null +++ b/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/ArkPluginJarTask.java @@ -0,0 +1,296 @@ +/* + * 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.alipay.sofa.ark.boot.mojo; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.zip.ZipFile; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ExcludeRule; +import org.gradle.api.artifacts.ResolvedArtifact; +import org.gradle.api.internal.file.copy.CopyAction; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskAction; +import org.gradle.jvm.tasks.Jar; + +public class ArkPluginJarTask extends Jar { + + private final ArkPluginExtension arkPluginExtension; + + private final Set filteredArtifacts; + private final Set conflictArtifacts; + + private final Set shadeFiles = new HashSet<>(); + + public ArkPluginJarTask(){ + super(); + Project project = getProject(); + arkPluginExtension = project.getExtensions().findByType(ArkPluginExtension.class); + + configureDestination(); + configurePluginJar(); + + from(project.getTasks().getByName("classes")); + + Set allArtifacts = getProjectArtifacts(); + filteredArtifacts = filterArtifacts1(allArtifacts); + conflictArtifacts = filterConflictArtifacts(filteredArtifacts); + + configureSourceSet(project); + configureArtifacts(); + handleConflictArtifacts(conflictArtifacts); + + configureManifest(project); + + addArkPluginMark(); + } + + private void configurePluginJar(){ + getArchiveFileName().set(getProject().provider(() -> { + String pluginName = arkPluginExtension.getPluginName().get(); + return pluginName + ".jar"; + })); + } + + private void configureArtifacts() { + into("lib", copySpec -> { + copySpec.from(getProject().provider(this::getFilteredArtifactFiles)); + copySpec.rename(this::renameArtifactIfConflict); + }); + + into("", copySpec -> { + copySpec.from(getProject().provider(() -> shadeFiles)); + }); + } + + private String renameArtifactIfConflict(String fileName) { + ResolvedArtifact artifact = findArtifactByFileName(fileName); + if (artifact != null && conflictArtifacts.contains(artifact)) { + return artifact.getModuleVersion().getId().getGroup() + "-" + fileName; + } + return fileName; + } + + private ResolvedArtifact findArtifactByFileName(String fileName) { + return filteredArtifacts.stream() + .filter(artifact -> artifact.getFile().getName().equals(fileName)) + .findFirst() + .orElse(null); + } + + private Set getFilteredArtifactFiles() { + Set shadeNames = arkPluginExtension.getShades().get().stream() + .map(this::getShadeFileName) + .collect(Collectors.toSet()); + + return filteredArtifacts.stream() + .filter(artifact -> { + boolean isShade = shadeNames.contains(artifact.getFile().getName()); + if (isShade) { + shadeFiles.add(artifact.getFile()); + } + return !isShade; + }) + .map(ResolvedArtifact::getFile) + .filter(this::isZip) + .collect(Collectors.toSet()); + } + + private String getShadeFileName(String shade) { + String[] parts = shade.split(":"); + if (parts.length != 3) { + throw new IllegalArgumentException("Invalid shade format: " + shade); + } + String name = parts[1]; + String version = parts[2]; + return name + "-" + version + ".jar"; + } + + private void configureManifest(Project project){ + Provider> manifestAttributes = project.provider(() -> { + Map attributes = new HashMap<>(); + attributes.put("Manifest-Version", "1.0"); + attributes.put("Ark-Plugin-Name", arkPluginExtension.getPluginName().get()); + attributes.put("Ark-Plugin-Version", project.getVersion().toString()); + + attributes.put("groupId", project.getGroup().toString()); + attributes.put("artifactId", project.getName()); + attributes.put("version", project.getVersion().toString()); + attributes.put("priority", arkPluginExtension.getPriority().get()); + attributes.put("pluginName", arkPluginExtension.getPluginName().get()); + attributes.put("description", arkPluginExtension.getDescription().get()); + attributes.put("activator", arkPluginExtension.getActivator().get()); + + attributes.putAll(arkPluginExtension.getImported().toAttributes("import")); + attributes.putAll(arkPluginExtension.getExported().toAttributes("export")); + return attributes; + }); + + getManifest().attributes(manifestAttributes.get()); + } + + private void configureArtifacts(Project project, Set filteredArtifacts){ + from(project.provider(() -> { + return filteredArtifacts.stream() + .map(ResolvedArtifact::getFile) + .collect(Collectors.toSet()); + })); + } + + private void configureDestination(){ + getDestinationDirectory().set(arkPluginExtension.getOutputDirectory()); + } + private void configureSourceSet(Project project){ + SourceSet mainSourceSet = project.getExtensions() + .getByType(JavaPluginExtension.class) + .getSourceSets() + .getByName(SourceSet.MAIN_SOURCE_SET_NAME); + from(mainSourceSet.getOutput()); + } + + private void configureCopySpec(Project project) { + + from(project.provider(() -> { + Configuration runtimeClasspath = project.getConfigurations().getByName("runtimeClasspath"); + Set artifacts = runtimeClasspath.getResolvedConfiguration().getResolvedArtifacts(); + return filterArtifacts(artifacts); + })); + + } + + private Set filterArtifacts1(Set artifacts) { + return artifacts.stream() + .filter(this::shouldIncludeArtifact) + .collect(Collectors.toSet()); + } + + + private Set filterArtifacts(Set artifacts) { + return artifacts.stream() + .filter(this::shouldIncludeArtifact) + .map(ResolvedArtifact::getFile) + .collect(Collectors.toSet()); + } + + private boolean shouldIncludeArtifact(ResolvedArtifact artifact) { + String groupId = artifact.getModuleVersion().getId().getGroup(); + String artifactId = artifact.getName(); + String gav = groupId + ":" + artifactId ; + + if (this.arkPluginExtension.getExcludes().get().contains(gav)) { + return false; + } + + if (this.arkPluginExtension.getExcludeGroupIds().get().contains(groupId)) { + return false; + } + + if (this.arkPluginExtension.getExcludeArtifactIds().get().contains(artifactId)) { + return false; + } + + return true; + } + + private Set getProjectArtifacts() { + Configuration configuration = getProject().getConfigurations().getByName("runtimeClasspath"); + return configuration.getResolvedConfiguration().getResolvedArtifacts(); + } + + private boolean isZip(File file) { + try (ZipFile zipFile = new ZipFile(file)) { + return true; + } catch (Exception e) { + return false; + } + } + + @Override + protected CopyAction createCopyAction() { + File jarFile = getArchiveFile().get().getAsFile(); + return new ArkPluginCopyAction(jarFile, shadeFiles); + } + + @TaskAction + private void action(){ + super.copy(); + } + + private void addArkPluginMark() { + String markContent = "this is plugin mark"; + String markPath = "com/alipay/sofa/ark/plugin"; + + from(getProject().provider(() -> { + try { + File tempFile = File.createTempFile("mark", null); + tempFile.deleteOnExit(); + Files.write(tempFile.toPath(), markContent.getBytes(StandardCharsets.UTF_8)); + return tempFile; + } catch (IOException e) { + throw new RuntimeException("Failed to create mark file", e); + } + }), copySpec -> { + copySpec.into(markPath); + copySpec.rename(fileName -> "mark"); + }); + } + + protected Set filterConflictArtifacts(Set artifacts) { + Project project = getProject(); + String projectArtifactId = project.getName(); + + Map existArtifacts = new HashMap<>(); + + existArtifacts.put(projectArtifactId, null); // ResolvedArtifact + + Set conflictArtifacts = new HashSet<>(); + + for (ResolvedArtifact artifact : artifacts) { + String artifactId = artifact.getName(); + if (existArtifacts.containsKey(artifactId)) { + conflictArtifacts.add(artifact); + ResolvedArtifact existingArtifact = existArtifacts.get(artifactId); + if (existingArtifact != null) { + conflictArtifacts.add(existingArtifact); + } + } else { + existArtifacts.put(artifactId, artifact); + } + } + return conflictArtifacts; + } + + private void handleConflictArtifacts(Set conflictArtifacts) { + for (ResolvedArtifact conflictArtifact : conflictArtifacts) { + getLogger().warn("Conflict artifact found: {}:{}:{}", + conflictArtifact.getModuleVersion().getId().getGroup(), + conflictArtifact.getName(), + conflictArtifact.getModuleVersion().getId().getVersion()); + } + } + +} diff --git a/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/BaseConfig.java b/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/BaseConfig.java new file mode 100644 index 000000000..b3b86d896 --- /dev/null +++ b/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/main/java/com/alipay/sofa/ark/boot/mojo/BaseConfig.java @@ -0,0 +1,61 @@ +/* + * 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.alipay.sofa.ark.boot.mojo; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +abstract public class BaseConfig { + private List packages = new ArrayList<>(); + private List classes = new ArrayList<>(); + private List resources = new ArrayList<>(); + + public List getPackages() { + return packages; + } + + public void setPackages(List packages) { + this.packages = packages; + } + + public List getClasses() { + return classes; + } + + public void setClasses(List classes) { + this.classes = classes; + } + + public List getResources() { + return resources; + } + + public void setResources(List resources) { + this.resources = resources; + } + + + public Map toAttributes(String prefix) { + Map attributes = new HashMap<>(); + attributes.put(prefix + "-packages", packages != null ? String.join(",", packages) : ""); + attributes.put(prefix + "-classes", classes != null ? String.join(",", classes) : ""); + attributes.put(prefix + "-resources", resources != null ? String.join(",", resources) : ""); + return attributes; + } +} diff --git a/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/test/java/com/alipay/sofa/ark/boot/mojo/ArkPluginExtensionTest.java b/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/test/java/com/alipay/sofa/ark/boot/mojo/ArkPluginExtensionTest.java new file mode 100644 index 000000000..4b0749fc6 --- /dev/null +++ b/sofa-ark-parent/support/ark-plugin-gradle-plugin/src/test/java/com/alipay/sofa/ark/boot/mojo/ArkPluginExtensionTest.java @@ -0,0 +1,64 @@ +/* + * 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.alipay.sofa.ark.boot.mojo; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import org.gradle.api.Project; +import org.gradle.testfixtures.ProjectBuilder; +import org.junit.Before; +import org.junit.Test; + +public class ArkPluginExtensionTest { + + private Project project; + private ArkPluginExtension extension; + + @Before + public void setup() { + project = ProjectBuilder.builder().withName("test-project").build(); + project.getPluginManager().apply("sofa-ark-plugin-gradle-plugin"); + extension = project.getExtensions().getByType(ArkPluginExtension.class); + } + + @Test + public void testDefaultValues() { + assertEquals("100", extension.getPriority().get()); + assertEquals("test-project", extension.getPluginName().get()); + assertEquals("", extension.getDescription().get()); + assertEquals("", extension.getActivator().get()); + assertTrue(extension.getOutputDirectory().get().getAsFile().getPath().endsWith("build" + File.separator + "libs")); + } + + @Test + public void testSetAndGetValues() { + extension.getPriority().set("200"); + extension.getPluginName().set("test-plugin"); + extension.getDescription().set("Test description"); + extension.getActivator().set("com.example.TestActivator"); + extension.getAttach().set(true); + + assertEquals("200", extension.getPriority().get()); + assertEquals("test-plugin", extension.getPluginName().get()); + assertEquals("Test description", extension.getDescription().get()); + assertEquals("com.example.TestActivator", extension.getActivator().get()); + assertTrue(extension.getAttach().get()); + } + +}