diff --git a/app/build.gradle b/app/build.gradle index 1b52650..4b20dfb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -37,7 +37,7 @@ dependencies { */ implementation component(':library') implementation component(':libraryKotlin') //需要如此申明才能享受编译时依赖sdk,打包时包括sdk及实现 - implementation component(':pins') //等价 project(':libraryWithoutPlugin') +// implementation component(':pins') //等价 project(':libraryWithoutPlugin') implementation component(':libraryWithoutPlugin') //等价 project(':libraryWithoutPlugin') } diff --git a/build.gradle b/build.gradle index fea2025..99a6a2c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,8 @@ apply from: "./gradleScript/config.gradle" buildscript { - ext.kotlin_version = '1.3.50' - ext.component_version = '1.0.8' + ext.kotlin_version = '1.6.21' + ext.component_version = '1.0.10' repositories { google() @@ -10,11 +10,11 @@ buildscript { maven { url 'https://dl.bintray.com/yummylau/maven' } } dependencies { - classpath 'com.android.tools.build:gradle:3.1.0' - classpath "com.effective.plugins:component:$component_version" + classpath 'com.android.tools.build:gradle:4.1.0' +// classpath "com.effective.plugins:component:$component_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" //调试需要 -// classpath "com.effective.plugins:component-plugin" + classpath "com.effective.plugins:component-plugin" //发布需要 classpath 'com.novoda:bintray-release:0.9.1' diff --git a/component-plugin/build.gradle b/component-plugin/build.gradle index d6a5f7c..b07fcb9 100644 --- a/component-plugin/build.gradle +++ b/component-plugin/build.gradle @@ -1,26 +1,37 @@ apply plugin: 'groovy' -apply plugin: 'maven' -apply plugin: 'com.novoda.bintray-release' +apply plugin: 'maven-publish' group = 'com.effective.plugins' archivesBaseName = 'component' -version = '1.0.8' +version = '1.0.10' -//设置本地发布路径 -uploadArchives { - repositories { - mavenDeployer { - /**配置Maven部署器,将工件上传到本地目录,这里是放到与该项目同级目录的repo目录下*/ - repository(url: uri('../repo')) +// Because the components are created only during the afterEvaluate phase, you must +// configure your publications using the afterEvaluate() lifecycle method. +afterEvaluate { + publishing { + publications { + release(MavenPublication) { + from components.java + + groupId = group + artifactId = archivesBaseName + version = version + } + } + repositories { + maven { + url = "../repo" + } } } } + dependencies { implementation gradleApi() implementation localGroovy() - implementation 'com.android.tools.build:gradle:3.1.0' - implementation 'org.jetbrains.kotlin:kotlin-compiler-embeddable:1.3.70' + implementation 'com.android.tools.build:gradle:4.1.0' + implementation 'org.jetbrains.kotlin:kotlin-compiler-embeddable:1.6.21' implementation 'org.ow2.asm:asm:7.1' implementation 'org.ow2.asm:asm-commons:7.1' implementation 'org.ow2.asm:asm-util:7.1' @@ -43,13 +54,13 @@ repositories { } -publish { - userOrg = 'yummylau' - groupId = 'com.effective.plugins' - artifactId = 'component' - publishVersion = '1.0.8' - desc = 'Android component plugin' - website = 'https://github.com/YummyLau/ComponentPlugin' -} +//publish { +// userOrg = 'yummylau' +// groupId = 'com.effective.plugins' +// artifactId = 'component' +// publishVersion = '1.0.8' +// desc = 'Android component plugin' +// website = 'https://github.com/YummyLau/ComponentPlugin' +//} diff --git a/component-plugin/src/main/groovy/com/plugin/component/ComponentPlugin.groovy b/component-plugin/src/main/groovy/com/plugin/component/ComponentPlugin.groovy index 8eea6fa..4998ce4 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/ComponentPlugin.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/ComponentPlugin.groovy @@ -1,12 +1,25 @@ package com.plugin.component import com.plugin.component.extension.ComponentExtension +import com.plugin.component.log.Logger import com.plugin.component.plugin.AbsPlugin import com.plugin.component.plugin.DebugPlugin import com.plugin.component.plugin.PinPlugin import com.plugin.component.plugin.SdkPlugin +import com.plugin.component.utils.ProjectUtil +import org.gradle.BuildListener +import org.gradle.api.Action import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.internal.dispatch.MethodInvocation +import org.gradle.internal.event.BroadcastDispatch +import org.gradle.internal.event.ListenerBroadcast +import org.gradle.invocation.DefaultGradle +import org.gradle.listener.ClosureBackedMethodInvocationDispatch + +import java.lang.reflect.Field +import java.lang.reflect.Method +import java.lang.reflect.Modifier /** * ./gradlew --no-daemon ComponentPlugin -Dorg.gradle.debug=true @@ -25,20 +38,39 @@ class ComponentPlugin extends AbsPlugin implements Plugin { private AbsPlugin debug = new DebugPlugin() private AbsPlugin pins = new PinPlugin() private ComponentExtension componentExtension + private boolean isFirst = true + static Project androidProject + static Project rootProject @Override void apply(Project project) { if (project == project.rootProject) { + rootProject = project.rootProject componentExtension = project.getExtensions().create(Constants.COMPONENT, ComponentExtension, project) initExtension(componentExtension) Runtimes.initRuntimeConfigurationOnEvaluate(project) project.afterEvaluate { + androidProject = ProjectUtil.getProject(project, componentExtension.appModule) Runtimes.initRuntimeConfigurationAfterEvaluate(project, componentExtension) - Runtimes.hookAfterApplyingAndroidPlugin(project, this) + Runtimes.hookAfterApplyingAndroidPlugin(project, androidProject, this) Runtimes.injectComponentPlugin(project) + Runtimes.registerPublishTask(project) } +// def projectsEvaluatedList = Runtimes.hookProjectsEvaluatedAction(project) project.gradle.projectsEvaluated { - afterAllEvaluate(project) + if (isFirst) { + isFirst = false + //修改引用 + afterAllEvaluate(project) +// //后执行移除的监听(主要调整执行顺序,重依赖才能生效和不报错,可能有AGP 版本兼容问题) +// Class clazz = Class.forName("org.gradle.api.invocation.Gradle") +// Method method = clazz.getDeclaredMethod("projectsEvaluated", Action.class) +// Object[] objects = [it] +// MethodInvocation mMethodInvocation = new MethodInvocation(method, objects) +// projectsEvaluatedList.forEach { +// it.dispatch(mMethodInvocation) +// } + } } } else { evaluateBeforeAndroidPlugin(project) @@ -73,9 +105,9 @@ class ComponentPlugin extends AbsPlugin implements Plugin { } @Override - void afterEvaluateAfterAndroidPlugin(Project project) { - sdk.afterEvaluateAfterAndroidPlugin(project) - debug.afterEvaluateAfterAndroidPlugin(project) + void afterEvaluateAfterAndroidPlugin(Project project, Project androidProject) { + sdk.afterEvaluateAfterAndroidPlugin(project, androidProject) + debug.afterEvaluateAfterAndroidPlugin(project, androidProject) } @Override diff --git a/component-plugin/src/main/groovy/com/plugin/component/Constants.groovy b/component-plugin/src/main/groovy/com/plugin/component/Constants.groovy index d1e0a9f..8f69cfc 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/Constants.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/Constants.groovy @@ -27,16 +27,18 @@ class Constants { public static String PLUGIN_CACHE = "scan_cache" //file - public static String DEFAULT_MAIN_MODULE_NAME = "app" + public static String DEFAULT_MAIN_MODULE_NAME = "main" //默认工程主项目名称 public static String SDK_DIR = '.gradle/component/sdk' //sdk存放目录 - public static String IMPL_DIR = 'build/component/impl' + public static String IMPL_DIR = '.gradle/component/impl' //impl存放目录 public static String BUILD_SDK_DIR = 'build/component/sdk' //生成的sdk文件的build目录 public static String BUILD_IMPL_DIR = 'build/component/impl' //生成的impl的build目录 + //发布日志路径 + public static String PUBLISH_LOG_FILE = "componentpublish.log" public static String BUILD_SOURCE_DIR = 'source' public static String BUILD_CLASSES_DIR = 'classes' public static String BUILD_OUTPUT_DIR = 'outputs' @@ -52,4 +54,29 @@ class Constants { public static String ASSETS_PATH = "src/main/assets" public static String JNILIBS_PATH = "src/main/jniLibs" public static String MANIFEST = "src/main/AndroidManifest.xml" + + //git hooks + public static String HOOKS_DIR = "hooks" + public static String HOOKS_CONTENT = +"""#!/bin/sh + +remote="\$1" +url="\$2" +branch=\$(git symbolic-ref --short -q HEAD) + +strB="BUILD SUCCESSFUL" +echo "current branch:\$branch" +tempLog=/tmp/\$(date +%s).log && touch \$tempLog +if [[ "\$branch" =~ (^develop\$)|(^release/*) ]]; then + ./gradlew ComponentPublishDebug | tee \$tempLog + result=\$(cat \$tempLog) + checkStr=\$(echo "\$result" | grep "\${strB}") + if [[ "\$checkStr" != "" ]]; then + exit 0; + else + exit 1; + fi +fi +exit 0; +""" } \ No newline at end of file diff --git a/component-plugin/src/main/groovy/com/plugin/component/FlatDirModuleComponentSelector.groovy b/component-plugin/src/main/groovy/com/plugin/component/FlatDirModuleComponentSelector.groovy index c08d4f9..de20ee0 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/FlatDirModuleComponentSelector.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/FlatDirModuleComponentSelector.groovy @@ -24,7 +24,7 @@ public class FlatDirModuleComponentSelector implements ModuleComponentSelector { private FlatDirModuleComponentSelector(ModuleIdentifier module, ImmutableVersionConstraint version, ImmutableAttributes attributes, List requestedCapabilities) { assert module != null: "module cannot be null" - assert version != null: "version cannot be null" + assert version != null: "sdkVersion cannot be null" assert attributes != null: "attributes cannot be null" diff --git a/component-plugin/src/main/groovy/com/plugin/component/Runtimes.groovy b/component-plugin/src/main/groovy/com/plugin/component/Runtimes.groovy index e2e1b3f..912a991 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/Runtimes.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/Runtimes.groovy @@ -21,13 +21,29 @@ import com.plugin.component.extension.option.sdk.SdkOption import com.plugin.component.log.Logger import com.plugin.component.log.MutLineLog import com.plugin.component.plugin.AbsPlugin +import com.plugin.component.task.ComponentPublishTask import com.plugin.component.transform.ComponentTransform +import com.plugin.component.utils.FileUtil +import com.plugin.component.utils.GitUtil import com.plugin.component.utils.JarUtil import com.plugin.component.utils.ProjectUtil import com.plugin.component.utils.PublicationUtil +import org.gradle.BuildListener import org.gradle.api.Action +import org.gradle.api.GradleException import org.gradle.api.Project +import org.gradle.api.Task import org.gradle.api.plugins.AppliedPlugin +import org.gradle.api.tasks.TaskExecutionException +import org.gradle.api.tasks.TaskProvider +import org.gradle.internal.event.BroadcastDispatch +import org.gradle.internal.event.ListenerBroadcast +import org.gradle.invocation.DefaultGradle +import org.gradle.listener.ClosureBackedMethodInvocationDispatch + +import java.lang.reflect.Field +import java.lang.reflect.Modifier +import java.util.concurrent.ConcurrentHashMap class Runtimes { @@ -36,7 +52,7 @@ class Runtimes { //impl 发布信息 private static Map sImplPublicationMap = new HashMap<>() //模块信息 - private static Map sProjectInfoMap = new HashMap<>() + private static Map sProjectInfoMap = new ConcurrentHashMap<>() private static Map sPinConfigurations = new HashMap<>() @@ -71,6 +87,7 @@ class Runtimes { sImplDir.mkdirs() } } + initGitHooks(root) root.repositories { flatDir { @@ -79,6 +96,7 @@ class Runtimes { } } PublicationManager.getInstance().loadManifest(root) + Logger.buildOutput("load Manifest") PublicationDependenciesOption.metaClass.component { String value -> return Constants.COMPONENT_PRE + value } @@ -94,6 +112,25 @@ class Runtimes { ) } + private static void initGitHooks(Project root) { + if (GitUtil.gitExist) { + File hooksDir = new File(root.projectDir, Constants.HOOKS_DIR) + if (!hooksDir.exists()) { + hooksDir.mkdirs() + } + File prePushHook = new File(hooksDir, "pre-push") + if (!prePushHook.exists()) { + prePushHook.createNewFile() + FileOutputStream fos = new FileOutputStream(prePushHook) + OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8") + osw.write(Constants.HOOKS_CONTENT) + osw.flush() + osw.close() + } + FileUtil.shell("git config core.hooksPath ${hooksDir.absolutePath} && chmod 700 ${hooksDir.absolutePath}/*") + } + } + static initRuntimeConfigurationAfterEvaluate(Project root, ComponentExtension componentExtension) { sExtension = componentExtension sDebugOption = sExtension.debugOption @@ -127,6 +164,23 @@ class Runtimes { } } + //添加flat路径 + root.allprojects.each { + if (it == root) return + if (!shouldApplyComponentPlugin(it)) return + Project childProject = it + ProjectInfo projectInfo = new ProjectInfo(childProject) + addProjectInfo(childProject.name, projectInfo) + childProject.repositories { + flatDir { + dirs sSdkDir.absolutePath + dirs sImplDir.absolutePath + mutLineLog.build4("* " + childProject.name + " add flatDir Dir[" + sSdkDir.absolutePath + "]") + mutLineLog.build4("* " + childProject.name + " add flatDir Dir[" + sImplDir.absolutePath + "]") + } + } + } + mutLineLog.build4("* 初始化 pins main 目录") List topSort = PublicationManager.getInstance().dependencyGraph.topSort() Collections.reverse(topSort) @@ -136,44 +190,42 @@ class Runtimes { return } Project childProject = root.findProject(publication.project) + ProjectInfo projectInfo = getProjectInfo(childProject.name) + if (projectInfo != null && projectInfo.isPublish || sSdkOption.isPublishMode) { + publication.sdkNeedPublish = true + publication.impNeedPublish = true + publication.forceUseLocal(true) + //发布时删除本地文件,确保不会使用本地文件 + File targetSdk = new File(sSdkDir, PublicationUtil.getJarName(publication)) + if (targetSdk.exists()) { + targetSdk.delete() + } + File targetImpl = new File(sImplDir, PublicationUtil.getAarName(publication)) + if (targetImpl.exists()) { + targetImpl.delete() + } + } PublicationUtil.filterPublicationDependencies(publication) - if (publication.version != null) { - JarUtil.handleMavenJar(childProject, publication) + if (!publication.forceLocal && publication.sdkVersion != null) { + mutLineLog.build4("* " + JarUtil.handleMavenJar(childProject, publication)) } else { mutLineLog.build4("* " + JarUtil.handleLocalJar(childProject, publication)) } PublicationManager.getInstance().hitPublication(publication) } - - //添加flat路径 - root.allprojects.each { - if (it == root) return - if (!shouldApplyComponentPlugin(it)) return - Project childProject = it - childProject.repositories { - flatDir { - dirs sSdkDir.absolutePath - mutLineLog.build4("* " + childProject.name + "add flatDir Dir[" + sSdkDir.absolutePath + "]") - dirs sImplDir.absolutePath - } - } - } - Logger.buildBlockLog("预处理插件", mutLineLog) } - static hookAfterApplyingAndroidPlugin(Project root, AbsPlugin... plugins) { + static hookAfterApplyingAndroidPlugin(Project root, Project androidProject, AbsPlugin... plugins) { MutLineLog mutLineLog = new MutLineLog() - root.allprojects.each { if (it == root) return if (!shouldApplyComponentPlugin(it)) return Project childProject = it - ProjectInfo projectInfo = new ProjectInfo(childProject) - addProjectInfo(childProject.name, projectInfo) + ProjectInfo projectInfo = getProjectInfo(it.name) mutLineLog.build4("* " + childProject.name) @@ -183,32 +235,10 @@ class Runtimes { mutLineLog.build4(" taskNames = " + projectInfo.taskNames) mutLineLog.build4(" isSyncTask = " + projectInfo.isSync()) mutLineLog.build4(" isAssemble = " + projectInfo.isAssemble) + mutLineLog.build4(" isPublish = " + projectInfo.isPublish) mutLineLog.build4(" isDebug = " + projectInfo.isDebug) -// -// childProject.plugins.all { -// Class extensionClass -// if (it instanceof AppPlugin) { -// extensionClass = AppExtension -// } else if (it instanceof LibraryPlugin) { -// extensionClass = LibraryExtension -// } else { -// return -// } -// childProject.extensions.configure(extensionClass, new Action() { -// @Override -// void execute(TestedExtension testedExtension) { -// if (plugins != null && plugins.size() > 0) { -// for (AbsPlugin absPlugin : plugins) { -// absPlugin.evaluateAfterAndroidPlugin(childProject) -// } -// } -// } -// }) -// } + mutLineLog.build4(" modules = " + projectInfo.modules) - /** - * 代替上述注解逻辑 - */ childProject.plugins.whenObjectAdded { if (it instanceof AppPlugin || it instanceof LibraryPlugin) { if (plugins != null && plugins.size() > 0) { @@ -226,7 +256,7 @@ class Runtimes { } if (plugins != null && plugins.size() > 0) { for (AbsPlugin absPlugin : plugins) { - absPlugin.afterEvaluateAfterAndroidPlugin(childProject) + absPlugin.afterEvaluateAfterAndroidPlugin(childProject, androidProject) } } } @@ -235,6 +265,65 @@ class Runtimes { Logger.buildBlockLog("子 PROJECT 信息", mutLineLog) } + //AGP4.1.0后调整了插件入口,废弃。 + //把所有 监听了 projectsEvaluated 的匿名内部类移除 + static List> hookProjectsEvaluatedAction(Project project) { + def removeDispatch = new ArrayList>() + try { + ListenerBroadcast buildListenerBroadcast + def fBuildListenerBroadcast = DefaultGradle.class.getDeclaredField("buildListenerBroadcast") + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true) + modifiersField.setInt(fBuildListenerBroadcast, fBuildListenerBroadcast.getModifiers() & ~Modifier.FINAL) + fBuildListenerBroadcast.setAccessible(true) + buildListenerBroadcast = (ListenerBroadcast) fBuildListenerBroadcast.get(project.gradle) + + Field fBroadcast = ListenerBroadcast.class.getDeclaredField("broadcast") + fBroadcast.setAccessible(true) + BroadcastDispatch broadcast = (BroadcastDispatch) fBroadcast.get(buildListenerBroadcast) + def fDispatchers = broadcast.getClass().getDeclaredField("dispatchers") + fDispatchers.setAccessible(true) + ArrayList> dispatchers = (ArrayList>) fDispatchers.get(broadcast) + + Class clazz = + Class.forName("org.gradle.internal.event.BroadcastDispatch\$ActionInvocationHandler") + Class clazz2 = + Class.forName("org.gradle.listener.ClosureBackedMethodInvocationDispatch") + + def iterator = dispatchers.iterator() + + while (iterator.hasNext()) { + try { + def next = iterator.next() + def fDispatch = next.getClass().getDeclaredField("dispatch") + fDispatch.setAccessible(true) + def dispatch = fDispatch.get(next) + Logger.buildOutput("dispatch:$dispatch") + def fMethodName + if (dispatch instanceof ClosureBackedMethodInvocationDispatch) { + fMethodName = clazz2.getDeclaredField("methodName") + } else if (dispatch.getClass() == clazz) { + fMethodName = clazz.getDeclaredField("methodName") + } else { + continue + } + fMethodName.setAccessible(true) + def methodName = fMethodName.get(dispatch) + if (methodName instanceof String && methodName.contains("projectsEvaluated")) { + removeDispatch.add(next) + iterator.remove() + } + } catch (Exception ignore) { + ignore.printStackTrace() + } + } + + } catch (Exception ignore) { + ignore.printStackTrace() + } + return removeDispatch + } + /** * 测试 AppPlugin/LibraryPlugin 时许 * t(evaluate) < t(configure) < t(whenObjectAdded) < t(withPlugin) < t(afterEvaluate) @@ -302,6 +391,33 @@ class Runtimes { Logger.buildBlockLog("子 PROJECT 注入插件", mutLineLog) } + static void registerPublishTask(Project root) { + TaskProvider debugTask = root.tasks.register("ComponentPublishDebug", ComponentPublishTask.class) + TaskProvider releaseTask = root.tasks.register("ComponentPublishRelease", ComponentPublishTask.class) + } + + static void checkPublishEnable(Project project) { + boolean shouldShutdown = false + project.rootProject.allprojects.each { childProject -> + if (childProject == project) return + if (!shouldApplyComponentPlugin(childProject)) return + + PublicationOption sdkPublication = getSdkPublication(childProject.name) + PublicationOption implPublication = getImplPublication(childProject.name) + + if (sdkPublication == null || implPublication == null) { + return + } + + if (!GitUtil.canPublish(sdkPublication.sdkSourceSet.path) || !GitUtil.canPublish(implPublication.impSourceSet.path)) { + shouldShutdown = true + } + } + if (shouldShutdown) { + throw new GradleException("Must commit and push changes before publish!!!") + } + } + static Map getPinConfigurations() { return sPinConfigurations @@ -377,4 +493,12 @@ class Runtimes { } return null } + + static void clean() { + sSdkPublicationMap.clear() + sImplPublicationMap.clear() + sProjectInfoMap.clear() + sPinConfigurations.clear() + sAssembleModules.clear() + } } diff --git a/component-plugin/src/main/groovy/com/plugin/component/extension/ComponentExtension.groovy b/component-plugin/src/main/groovy/com/plugin/component/extension/ComponentExtension.groovy index 8362c8e..34dc703 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/extension/ComponentExtension.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/extension/ComponentExtension.groovy @@ -22,6 +22,8 @@ class ComponentExtension { Set validModules Project project + String appModule //app模块 + ComponentExtension(Project project) { this.project = project debugOption = new DebugOption(project) @@ -61,6 +63,16 @@ class ComponentExtension { } } + /** + * 配置app主模块名称 + * @param moduleName + */ + void app(String... modules) { + if(modules != null && modules.size() > 0){ + this.appModule = modules[0] + } + + } /** * sdk模块 @@ -118,6 +130,7 @@ class ComponentExtension { String toString() { StringBuilder stringBuilder = new StringBuilder("\n") stringBuilder.append(" ------------------------------------------------------------------" + "\n") + stringBuilder.append(" | appModule = " + appModule.toString() + "\n") stringBuilder.append(" | include = " + includeModules.toList().toString() + "\n") stringBuilder.append(" | exclude = " + excludeModules.toList().toString() + "\n") stringBuilder.append(" | Select by " + (!includeModules.isEmpty() ? "includeModel" : "excludeModel") + "\n") diff --git a/component-plugin/src/main/groovy/com/plugin/component/extension/PublicationManager.groovy b/component-plugin/src/main/groovy/com/plugin/component/extension/PublicationManager.groovy index c2374d2..a79c8b2 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/extension/PublicationManager.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/extension/PublicationManager.groovy @@ -1,12 +1,18 @@ package com.plugin.component.extension import com.plugin.component.Constants +import com.plugin.component.extension.module.ProjectInfo +import com.plugin.component.extension.option.sdk.SdkOption import com.plugin.component.log.Logger import com.plugin.component.Runtimes import com.plugin.component.extension.module.Digraph import com.plugin.component.extension.module.SourceFile import com.plugin.component.extension.module.SourceSet import com.plugin.component.extension.option.sdk.PublicationOption +import com.plugin.component.log.MutLineLog +import com.plugin.component.utils.GitUtil +import com.plugin.component.utils.HttpUrlConnectHelper +import com.plugin.component.utils.ProjectUtil import com.plugin.component.utils.PublicationUtil import org.gradle.api.GradleException import org.gradle.api.Project @@ -20,6 +26,8 @@ import javax.xml.transform.Transformer import javax.xml.transform.TransformerFactory import javax.xml.transform.dom.DOMSource import javax.xml.transform.stream.StreamResult +import java.nio.charset.Charset +import java.text.SimpleDateFormat /** * 发布sdk管理 @@ -49,31 +57,127 @@ class PublicationManager { void loadManifest(Project rootProject) { sdkPublicationManifest = new HashMap<>() + implPublicationManifest = new HashMap<>() dependencyGraph = new Digraph() publicationDependencies = new HashMap<>() + boolean isAssemble = false + boolean isPublish = false + List taskNames = ProjectUtil.getTasks(rootProject) + if (!taskNames.isEmpty()) { + for (String task : taskNames) { + if (task.toUpperCase().contains("ASSEMBLE") + || task.contains("aR") + || task.contains("asR") + || task.contains("asD") + || task.toUpperCase().contains("TINKER") + || task.toUpperCase().contains("INSTALL") + || task.toUpperCase().contains("RESGUARD")) { + isAssemble = true + } else if (task.toUpperCase().contains("COMPONENTPUBLISH")) { + isPublish = true + } + } + } + rootProject.gradle.buildFinished { if (it.failure != null) { Logger.buildOutput("build fail!") return } - saveManifest() + saveManifest(isAssemble) + logPublishInfo(rootProject, isPublish || isAssemble) + Runtimes.clean() Logger.buildOutput("build finished!") } + loadSdkManifest() + if (isAssemble) { + loadImpManifest() + } + } - File sdkPublicationManifest = new File(Runtimes.sSdkDir, 'publicationManifest.xml') - File implPublicationManifest = new File(Runtimes.sImplDir, 'publicationManifest.xml') - if (!sdkPublicationManifest.exists()) { + private static void logPublishInfo(Project rootProject, boolean needLogTask) { + if (!needLogTask) { return } + boolean isPublish = Runtimes.sSdkOption.isPublishMode + MutLineLog mutLineLog = new MutLineLog() + MutLineLog mutLineLogFile = new MutLineLog() - if (!implPublicationManifest.exists()) { + rootProject.allprojects.each { childProject -> + if (!Runtimes.shouldApplyComponentPlugin(childProject)) return + + PublicationOption sdkPublication = Runtimes.getSdkPublication(childProject.name) + PublicationOption implPublication = Runtimes.getImplPublication(childProject.name) + ProjectInfo projectInfo = Runtimes.getProjectInfo(childProject.name) + if (sdkPublication == null || implPublication == null || projectInfo == null) { + return + } + isPublish = isPublish || projectInfo.isPublish + mutLineLog.build4("***********************************************************************************************************") + mutLineLog.build4("* SDK ${sdkPublication.name} publication=${PublicationUtil.getMavenGAV(sdkPublication)} publish=${sdkPublication.sdkNeedPublish}") + mutLineLog.build4("* commitInfo:") + mutLineLog.build4("* ${sdkPublication.sdkSourceSet.gitCommitInfo}") + mutLineLog.build4("* commitTime:${sdkPublication.sdkSourceSet.commitTime} commitUser:${sdkPublication.sdkSourceSet.commitUser}") + mutLineLog.build4("* ") + mutLineLog.build4("* Impl ${implPublication.name} publication=${PublicationUtil.getImpMavenGAV(implPublication, !projectInfo.isDebug)} publish=${implPublication.impNeedPublish}") + mutLineLog.build4("* commitInfo:") + mutLineLog.build4("* ${implPublication.impSourceSet.gitCommitInfo}") + mutLineLog.build4("* commitTime:${implPublication.impSourceSet.commitTime} commitUser:${implPublication.impSourceSet.commitUser}") + + if (sdkPublication.sdkNeedPublish || implPublication.impNeedPublish) { + mutLineLogFile.build("***********************************************************************************************************") + if (sdkPublication.sdkNeedPublish) { + mutLineLogFile.build("* SDK ${sdkPublication.name} publication=${PublicationUtil.getMavenGAV(sdkPublication)} publish=${sdkPublication.sdkNeedPublish}") + mutLineLogFile.build("* commitInfo:") + mutLineLogFile.build("* ${sdkPublication.sdkSourceSet.gitCommitInfo}") + mutLineLogFile.build("* commitTime:${sdkPublication.sdkSourceSet.commitTime} commitUser:${sdkPublication.sdkSourceSet.commitUser}") + } + if (implPublication.impNeedPublish) { + mutLineLogFile.build("* Impl ${implPublication.name} publication=${PublicationUtil.getImpMavenGAV(implPublication, !projectInfo.isDebug)} publish=${implPublication.impNeedPublish}") + mutLineLogFile.build("* commitInfo:") + mutLineLogFile.build("* ${implPublication.impSourceSet.gitCommitInfo}") + mutLineLogFile.build("* commitTime:${implPublication.impSourceSet.commitTime} commitUser:${implPublication.impSourceSet.commitUser}") + } + } + } + mutLineLog.build4("***********************************************************************************************************") + if (!mutLineLogFile.done().isEmpty()) { + mutLineLogFile.build("***********************************************************************************************************") + } + if (isPublish) { + Logger.buildBlockLog("Publish Info", mutLineLog) + if (mutLineLogFile.done().isEmpty()) { + return + } + /* + * http://10.0.9.238:8088/probe/plugin-publish-log?log=${long} + * 每次日志通过log传递 + * http://10.0.9.238:8080/godlike/plugin-logs/publish.log + * 浏览器打开查看 + */ + def format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") + StringBuilder builder = new StringBuilder() + String publishTile = "Publish Components in ${format.format(new Date())}" + builder.append(" ↓↓↓↓↓↓↓↓↓↓ $publishTile ↓↓↓↓↓↓↓↓↓↓\n") + builder.append(mutLineLogFile.done() + "\n") + builder.append(" ↑↑↑↑↑↑↑↑↑↑ $publishTile ↑↑↑↑↑↑↑↑↑↑\n") + String base = new String(Base64.urlEncoder.encode(builder.toString().getBytes("utf-8")), "utf-8") + HttpUrlConnectHelper.sendRequest("http://10.0.9.238:8088/probe/plugin-publish-log?log=${base}", "GET") + Logger.buildOutput("See http://10.0.9.238:8088/probe/log-query for more detail logs!") + } + } + + private void loadSdkManifest() { + File sdkPublicationManifestFile = new File(Runtimes.sSdkDir, 'publicationManifest.xml') + + if (!sdkPublicationManifestFile.exists()) { return } DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance() - Document document = builderFactory.newDocumentBuilder().parse(sdkPublicationManifest) + Document document = builderFactory.newDocumentBuilder().parse(sdkPublicationManifestFile) //搜寻所有publication节点 NodeList publicationNodeList = document.documentElement.getElementsByTagName("publication") @@ -85,8 +189,8 @@ class PublicationManager { publication.project = publicationElement.getAttribute("project") publication.groupId = publicationElement.getAttribute("groupId") publication.artifactId = publicationElement.getAttribute("artifactId") - publication.version = publicationElement.getAttribute("version") - if (publication.version == "") publication.version = null + publication.sdkVersion = publicationElement.getAttribute("version") + if (publication.sdkVersion == "") publication.sdkVersion = null publication.invalid = Boolean.valueOf(publicationElement.getAttribute("invalid")) //如果有效 @@ -96,6 +200,12 @@ class PublicationManager { SourceSet sourceSet = new SourceSet() sourceSet.path = sourceSetElement.getAttribute("path") sourceSet.lastModifiedSourceFile = new HashMap<>() + sourceSet.gitVersion = sourceSetElement.getAttribute("gitVersion") + try { + sourceSet.quickVerifyModified = sourceSetElement.getAttribute("quickVerifyModified").toLong() + } catch (Exception ignored) { + sourceSet.quickVerifyModified = 0L + } NodeList fileNodeList = sourceSetElement.getElementsByTagName("file") for (int k = 0; k < fileNodeList.getLength(); k++) { Element fileElement = (Element) fileNodeList.item(k) @@ -104,23 +214,131 @@ class PublicationManager { sourceFile.lastModified = fileElement.getAttribute("lastModified").toLong() sourceSet.lastModifiedSourceFile.put(sourceFile.path, sourceFile) } - publication.misSourceSet = sourceSet + publication.sdkSourceSet = sourceSet } this.sdkPublicationManifest.put(PublicationUtil.getPublicationId(publication), publication) } + } + + private void loadImpManifest() { + File implPublicationManifestFile = new File(Runtimes.sImplDir, 'publicationManifest.xml') + if (!implPublicationManifestFile.exists()) { + return + } + + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance() + Document document = builderFactory.newDocumentBuilder().parse(implPublicationManifestFile) + + //搜寻所有publication节点 + NodeList publicationNodeList = document.documentElement.getElementsByTagName("publication") + for (int i = 0; i < publicationNodeList.getLength(); i++) { + + Element publicationElement = (Element) publicationNodeList.item(i) + + PublicationOption publication = new PublicationOption() + publication.project = publicationElement.getAttribute("project") + publication.groupId = publicationElement.getAttribute("groupId") + publication.artifactId = publicationElement.getAttribute("artifactId") + publication.implVersion = publicationElement.getAttribute("version") + if (publication.implVersion == "") publication.implVersion = null + publication.invalid = Boolean.valueOf(publicationElement.getAttribute("invalid")) + + //如果有效 + if (!publication.invalid) { + NodeList sourceSetNodeList = publicationElement.getElementsByTagName("sourceSet") + Element sourceSetElement = (Element) sourceSetNodeList.item(0) + SourceSet sourceSet = new SourceSet() + sourceSet.path = sourceSetElement.getAttribute("path") + sourceSet.lastModifiedSourceFile = new HashMap<>() + sourceSet.gitVersion = sourceSetElement.getAttribute("gitVersion") + try { + sourceSet.quickVerifyModified = sourceSetElement.getAttribute("quickVerifyModified").toLong() + } catch (Exception ignored) { + sourceSet.quickVerifyModified = 0L + } + NodeList fileNodeList = sourceSetElement.getElementsByTagName("file") + for (int k = 0; k < fileNodeList.getLength(); k++) { + Element fileElement = (Element) fileNodeList.item(k) + SourceFile sourceFile = new SourceFile() + sourceFile.path = fileElement.getAttribute("path") + sourceFile.lastModified = fileElement.getAttribute("lastModified").toLong() + sourceSet.lastModifiedSourceFile.put(sourceFile.path, sourceFile) + } + publication.impSourceSet = sourceSet + } + + this.implPublicationManifest.put(PublicationUtil.getPublicationId(publication), publication) + } + + } + + + private void saveManifest(boolean isAssemble) { + if (!Runtimes.sSdkDir.exists()) { + Runtimes.sSdkDir.mkdirs() + } + saveSdkManifest() + + if (isAssemble) { + if (!Runtimes.sImplDir.exists()) { + Runtimes.sImplDir.mkdirs() + } + saveImpManifest() + } } + /** + * 保存 impPublicationManifest.xml + */ + private void saveImpManifest() { + File implPublicationManifest = new File(Runtimes.sImplDir, 'publicationManifest.xml') + + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance() + Document document = builderFactory.newDocumentBuilder().newDocument() + Element manifestElement = document.createElement("manifest") + this.implPublicationManifest.each { + PublicationOption publication = it.value + + if (!publication.hit || publication.invalid) return + + Element publicationElement = document.createElement('publication') + publicationElement.setAttribute('project', publication.project) + publicationElement.setAttribute('groupId', publication.groupId) + publicationElement.setAttribute('artifactId', publication.artifactId) + publicationElement.setAttribute('version', publication.implVersion) + publicationElement.setAttribute('invalid', publication.invalid ? "true" : "false") + + if (!publication.invalid) { + Element sourceSetElement = document.createElement('sourceSet') + sourceSetElement.setAttribute('path', publication.impSourceSet.path) + sourceSetElement.setAttribute('quickVerifyModified', publication.impSourceSet.quickVerifyModified.toString()) + sourceSetElement.setAttribute("gitVersion", publication.impSourceSet.gitVersion) +// publication.impSourceSet.lastModifiedSourceFile.each { +// SourceFile sourceFile = it.value +// Element sourceFileElement = document.createElement('file') +// sourceFileElement.setAttribute('path', sourceFile.path) +// sourceFileElement.setAttribute('lastModified', sourceFile.lastModified.toString()) +// sourceSetElement.appendChild(sourceFileElement) +// } + publicationElement.appendChild(sourceSetElement) + } + manifestElement.appendChild(publicationElement) + } + + Transformer transformer = TransformerFactory.newInstance().newTransformer() + transformer.setOutputProperty(OutputKeys.INDENT, "yes") + transformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, "yes") + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2") + transformer.transform(new DOMSource(manifestElement), new StreamResult(implPublicationManifest)) + } + /** * 保存 sdkPublicationManifest.xml */ - private void saveManifest() { - if (!Runtimes.sSdkDir.exists()) { - Runtimes.sSdkDir.mkdirs() - } + private void saveSdkManifest() { File sdkPublicationManifestFile = new File(Runtimes.sSdkDir, 'publicationManifest.xml') - File implPublicationManifest = new File(Runtimes.sSdkDir, 'publicationManifest.xml') DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance() Document document = builderFactory.newDocumentBuilder().newDocument() @@ -134,19 +352,21 @@ class PublicationManager { publicationElement.setAttribute('project', publication.project) publicationElement.setAttribute('groupId', publication.groupId) publicationElement.setAttribute('artifactId', publication.artifactId) - publicationElement.setAttribute('version', publication.version) + publicationElement.setAttribute('version', publication.sdkVersion) publicationElement.setAttribute('invalid', publication.invalid ? "true" : "false") if (!publication.invalid) { Element sourceSetElement = document.createElement('sourceSet') - sourceSetElement.setAttribute('path', publication.misSourceSet.path) - publication.misSourceSet.lastModifiedSourceFile.each { - SourceFile sourceFile = it.value - Element sourceFileElement = document.createElement('file') - sourceFileElement.setAttribute('path', sourceFile.path) - sourceFileElement.setAttribute('lastModified', sourceFile.lastModified.toString()) - sourceSetElement.appendChild(sourceFileElement) - } + sourceSetElement.setAttribute('path', publication.sdkSourceSet.path) + sourceSetElement.setAttribute('quickVerifyModified', publication.sdkSourceSet.quickVerifyModified.toString()) + sourceSetElement.setAttribute("gitVersion", publication.sdkSourceSet.gitVersion) +// publication.sdkSourceSet.lastModifiedSourceFile.each { +// SourceFile sourceFile = it.value +// Element sourceFileElement = document.createElement('file') +// sourceFileElement.setAttribute('path', sourceFile.path) +// sourceFileElement.setAttribute('lastModified', sourceFile.lastModified.toString()) +// sourceSetElement.appendChild(sourceFileElement) +// } publicationElement.appendChild(sourceSetElement) } manifestElement.appendChild(publicationElement) @@ -166,11 +386,14 @@ class PublicationManager { if (publication.dependencies != null) { if (publication.dependencies.implementation != null) { publication.dependencies.implementation.each { +// Logger.buildOutput("addDependencyGraph: project:${projectName} dependency: ${it}") if (it instanceof String && it.startsWith(Constants.COMPONENT_PRE)) { - String sdkDependency = PublicationUtil.getPublication(publication) +// String sdkDependency = PublicationUtil.getPublication(publication) + String sdkDependency = PublicationUtil.parseComponentToPublicationId(it) +// Logger.buildOutput("add sdkDependency project:${projectName} dependency: ${sdkDependency}") dependencyGraph.add(key, sdkDependency) if (!dependencyGraph.isDag()) { - throw new RuntimeException("Circular dependency in project [${projectName}] with '${sdkDependency}'.") + throw new RuntimeException("Circular dependency in project [${projectName}] with '${it}'.") } } } @@ -179,7 +402,8 @@ class PublicationManager { if (publication.dependencies.compileOnly != null) { publication.dependencies.compileOnly.each { if (it instanceof String && it.startsWith(Constants.COMPONENT_PRE)) { - String sdkDependency = PublicationUtil.getPublication(publication) +// String sdkDependency = PublicationUtil.getPublication(publication) + String sdkDependency = PublicationUtil.parseComponentToPublicationId(it) dependencyGraph.add(key, sdkDependency) if (!dependencyGraph.isDag()) { throw new RuntimeException("Circular dependency in project [${projectName}] with '${sdkDependency}'.") @@ -195,19 +419,56 @@ class PublicationManager { * @param publication * @return */ - boolean hasModified(PublicationOption publication) { + boolean hasSdkModified(PublicationOption publication) { + PublicationOption lastPublication = sdkPublicationManifest.get(PublicationUtil.getPublicationId(publication)) +// Logger.buildOutput("lastPublication:${lastPublication}") +// Logger.buildOutput("currentPublication:${publication}") if (lastPublication == null) { return true } if (publication.invalid != lastPublication.invalid) { return true } - return hasModifiedSourceSet(publication.misSourceSet, lastPublication.misSourceSet) + return hasModifiedSourceSet(publication.sdkSourceSet, lastPublication.sdkSourceSet) + } + + /** + * 判断是否修改 + * @param publication + * @return + */ + boolean hasImpModified(PublicationOption publication) { +// File file = new File(Runtimes.sImplDir, PublicationUtil.getAarName(publication)) +// if (!file.exists()) { +// return true +// } + PublicationOption lastPublication = implPublicationManifest.get(PublicationUtil.getPublicationId(publication)) +// Logger.buildOutput("lastPublication:${lastPublication}") +// Logger.buildOutput("currentPublication:${publication}") + if (lastPublication == null) { + return true + } + if (publication.invalid != lastPublication.invalid) { + return true + } + return hasModifiedSourceSet(publication.impSourceSet, lastPublication.impSourceSet) + } + + boolean hasModifyWithGitDiff(SourceSet sourceSet) { + if (!GitUtil.gitExist){ + return false + } + String gitStatus = GitUtil.getGitDiff(sourceSet.path) + if (gitStatus == null || gitStatus.isEmpty()) { + return false + } + return true } private boolean hasModifiedSourceSet(SourceSet sourceSet1, SourceSet sourceSet2) { - return hasModifiedSourceFile(sourceSet1.lastModifiedSourceFile, sourceSet2.lastModifiedSourceFile) + return sourceSet1.quickVerifyModified != sourceSet2.quickVerifyModified +// return hasModifiedSourceFile(sourceSet1.lastModifiedSourceFile, sourceSet2.lastModifiedSourceFile) } private boolean hasModifiedSourceFile(Map map1, Map map2) { @@ -231,6 +492,10 @@ class PublicationManager { sdkPublicationManifest.put(PublicationUtil.getPublicationId(publication), publication) } + void addImpPublication(PublicationOption publication) { + implPublicationManifest.put(PublicationUtil.getPublicationId(publication), publication) + } + PublicationOption getPublication(String groupId, String artifactId) { return sdkPublicationManifest.get(PublicationUtil.getPublicationId(groupId, artifactId)) } @@ -252,6 +517,30 @@ class PublicationManager { return publications } + PublicationOption getSdkPublicationByProject(Project project) { + String displayName = project.getDisplayName() + String projectName = displayName.substring(displayName.indexOf("'") + 1, displayName.lastIndexOf("'")) + PublicationOption publicationOption = null + sdkPublicationManifest.each { + if (projectName == it.value.project) { + publicationOption = it.value + } + } + return publicationOption + } + + PublicationOption getImplPublicationByProject(Project project) { + String displayName = project.getDisplayName() + String projectName = displayName.substring(displayName.indexOf("'") + 1, displayName.lastIndexOf("'")) + PublicationOption publicationOption = null + implPublicationManifest.each { + if (projectName == it.value.project) { + publicationOption = it.value + } + } + return publicationOption + } + void hitPublication(PublicationOption publication) { PublicationOption existsPublication = sdkPublicationManifest.get(PublicationUtil.getPublicationId(publication)) if (existsPublication == null) return diff --git a/component-plugin/src/main/groovy/com/plugin/component/extension/module/ProjectInfo.groovy b/component-plugin/src/main/groovy/com/plugin/component/extension/module/ProjectInfo.groovy index 889dd36..b7117a5 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/extension/module/ProjectInfo.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/extension/module/ProjectInfo.groovy @@ -20,6 +20,7 @@ class ProjectInfo { public String taskNames public List modules = new ArrayList<>() //包含的模块 public boolean isAssemble = false //是否是asAssemble + public boolean isPublish = false //是否发布 public boolean isDebug = false //是否是debug public String compileModuleName = "" //入口模块名字 public Set componentDependencies = new HashSet<>() //模块依赖的component @@ -61,6 +62,11 @@ class ProjectInfo { } String[] strings = task.split(":") modules.add(strings.length > 1 ? strings[strings.length - 2] : "all") + } else if (task.toUpperCase().contains("COMPONENTPUBLISH")) { + isPublish = true + if (task.toUpperCase().contains("DEBUG")) { + isDebug = true + } } } } diff --git a/component-plugin/src/main/groovy/com/plugin/component/extension/module/SourceSet.groovy b/component-plugin/src/main/groovy/com/plugin/component/extension/module/SourceSet.groovy index bedc7f0..d83aa48 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/extension/module/SourceSet.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/extension/module/SourceSet.groovy @@ -7,4 +7,9 @@ package com.plugin.component.extension.module class SourceSet { String path // 路径 Map lastModifiedSourceFile // 源码修改的资源集 + Long quickVerifyModified // 文件是否快速校验 + String gitVersion //当前git版本 + String gitCommitInfo //git提交信息 + String commitUser //git提交者 + String commitTime //git提交时间 } diff --git a/component-plugin/src/main/groovy/com/plugin/component/extension/option/sdk/PublicationDependenciesOption.groovy b/component-plugin/src/main/groovy/com/plugin/component/extension/option/sdk/PublicationDependenciesOption.groovy index d167576..1481373 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/extension/option/sdk/PublicationDependenciesOption.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/extension/option/sdk/PublicationDependenciesOption.groovy @@ -1,5 +1,7 @@ package com.plugin.component.extension.option.sdk +import org.gradle.util.ConfigureUtil + /** * sdk 依赖记录 * created by yummylau 2019/08/09 @@ -22,4 +24,22 @@ class PublicationDependenciesOption { } compileOnly.add(value) } + + void implementation(Closure closure) { + PublicationDependencyModuleOption option = new PublicationDependencyModuleOption() + ConfigureUtil.configure(closure, option) + if (implementation == null) { + implementation = new ArrayList<>() + } + implementation.add(option) + } + + void compileOnly(Closure closure) { + PublicationDependencyModuleOption option = new PublicationDependencyModuleOption() + ConfigureUtil.configure(closure, option) + if (compileOnly == null) { + compileOnly = new ArrayList<>() + } + compileOnly.add(option) + } } diff --git a/component-plugin/src/main/groovy/com/plugin/component/extension/option/sdk/PublicationDependencyModuleOption.groovy b/component-plugin/src/main/groovy/com/plugin/component/extension/option/sdk/PublicationDependencyModuleOption.groovy new file mode 100644 index 0000000..164b53a --- /dev/null +++ b/component-plugin/src/main/groovy/com/plugin/component/extension/option/sdk/PublicationDependencyModuleOption.groovy @@ -0,0 +1,17 @@ +package com.plugin.component.extension.option.sdk + +/** + * 依赖实现处理 + */ +class PublicationDependencyModuleOption { + String path + Map exclude + + void path(String value) { + this.path = value + } + + void exclude(Map value) { + this.exclude = value + } +} \ No newline at end of file diff --git a/component-plugin/src/main/groovy/com/plugin/component/extension/option/sdk/PublicationOption.groovy b/component-plugin/src/main/groovy/com/plugin/component/extension/option/sdk/PublicationOption.groovy index a3c3c2c..85c7809 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/extension/option/sdk/PublicationOption.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/extension/option/sdk/PublicationOption.groovy @@ -14,20 +14,30 @@ class PublicationOption { String scrName = "main" //资源目录名 String sourceSetName //sourceSet名 File buildDir //构建目录 + File impDir //imp实现目录 String project //模块project路径 - SourceSet misSourceSet //资源集,如果当前maven不可用,则需要记录源码及最后修改时间信息 + SourceSet sdkSourceSet //sdk资源集,如果当前maven不可用,则需要记录源码及最后修改时间信息 + SourceSet impSourceSet //imp资源集 String versionNew //新版本 boolean isSdk //是否时sdk boolean invalid //是否非法 boolean hit //是否隐藏 boolean useLocal //是否使用本地jar - + boolean forceLocal //强制使用本地打包 + boolean useUserSdkVersion //使用用户指定Sdk版本 + boolean useUserImplVersion //使用用户指定Impl版本 + boolean impNeedPack //实现aar是否需要重新打包 + boolean sdkNeedPublish = true //SDK是否需要发布 + boolean impNeedPublish = true //imp是否需要发布 + boolean useLocalImp //是否使用本地aar + boolean online = false //是否正式环境 /** * option 用于配置sdk资源 */ String groupId //依赖分组id String artifactId //依赖id - String version //依赖版本 + String sdkVersion //依赖版本 + String implVersion //impl版本 String localProject //依赖版本 Closure sourceFilter //资源过滤 PublicationDependenciesOption dependencies //所持有的依赖信息 @@ -49,8 +59,19 @@ class PublicationOption { this.artifactId = artifactId } - void version(String version) { - this.version = version + void sdkVersion(String version) { + this.sdkVersion = version + this.useUserSdkVersion = true + } + + void implVersion(String version) { + this.implVersion = version + this.useUserImplVersion = true + } + + void forceUseLocal(boolean forceUseLocal) { + this.forceLocal = forceUseLocal + this.useLocal = true } void dependencies(Closure closure) { @@ -61,4 +82,40 @@ class PublicationOption { void sourceFilter(Closure closure) { this.sourceFilter = closure } + + String getArtifactIdString() { + return "${artifactId}${online ? '' : "-test"}" + } + + @Override + public String toString() { + def sdkQuickVerify = "" + if (sdkSourceSet != null) { + sdkQuickVerify = sdkSourceSet.quickVerifyModified + } + def implQuickVerify = "" + if (impSourceSet != null) { + implQuickVerify = impSourceSet.quickVerifyModified + } + return "PublicationOption{" + + "name='" + name + '\'' + + ", scrName='" + scrName + '\'' + + ", sourceSetName='" + sourceSetName + '\'' + + ", buildDir=" + buildDir + + ", impDir=" + impDir + + ", project='" + project + '\'' + + ", versionNew='" + versionNew + '\'' + + ", isSdk=" + isSdk + + ", invalid=" + invalid + + ", hit=" + hit + + ", useLocal=" + useLocal + + ", impNeedPack=" + impNeedPack + + ", groupId='" + groupId + '\'' + + ", artifactId='" + artifactId + '\'' + + ", sdkVersion='" + sdkVersion + '\'' + + ", localProject='" + localProject + '\'' + + ", sdkQuickVerify='" + sdkQuickVerify + '\'' + + ", implQuickVerify='" + implQuickVerify + '\'' + + '}'; + } } diff --git a/component-plugin/src/main/groovy/com/plugin/component/extension/option/sdk/SdkOption.groovy b/component-plugin/src/main/groovy/com/plugin/component/extension/option/sdk/SdkOption.groovy index 1cb83e1..dea4c27 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/extension/option/sdk/SdkOption.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/extension/option/sdk/SdkOption.groovy @@ -1,14 +1,18 @@ package com.plugin.component.extension.option.sdk +import com.plugin.component.Constants import com.plugin.component.log.Logger import com.plugin.component.Runtimes import com.plugin.component.extension.PublicationManager +import com.plugin.component.utils.FileUtil +import com.plugin.component.utils.GitUtil import com.plugin.component.utils.ProjectUtil import com.plugin.component.utils.PublicationUtil import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project import org.gradle.api.artifacts.dsl.RepositoryHandler +import org.gradle.api.artifacts.repositories.MavenArtifactRepository import org.gradle.util.ConfigureUtil class SdkOption { @@ -16,7 +20,11 @@ class SdkOption { Project project int compileSdkVersion //编译版本 CompileOptions compileOption //编译选项 - Action configure //仓库配置 + Action repositories //仓库配置 + boolean isAutoVersion //是否使用自动版本 + boolean isPublishMode //是否发布模式 + boolean isOnline + String mavenUrl //maven仓库 SdkOption(Project project) { this.project = project @@ -24,6 +32,10 @@ class SdkOption { } + void mavenUrl(String mavenUrl) { + this.mavenUrl = mavenUrl + } + /** * 编译sdk * @param version @@ -46,7 +58,44 @@ class SdkOption { * @param configure */ void repositories(Action configure) { - this.configure = configure + this.repositories = configure + } + + /** + * 仓库配置 + * @param closure + */ + void repositories(Closure closure) { + this.repositories(ConfigureUtil.configureUsing(closure)) + } + + /** + * 是否使用自动版本 + * @param flag + */ + void autoVersion(boolean flag) { + isAutoVersion = flag + if (flag) { + if (!GitUtil.gitExist) { + throw RuntimeException("git command not found! Please check your git path") + } + } + } + + /** + * 是否发布模式 + * @param flag + */ + void publishMode(boolean flag) { + this.isPublishMode = flag + } + + /** + * 是否线上环境 + * @param flag + */ + void online(boolean flag) { + this.isOnline = flag } /** @@ -60,11 +109,32 @@ class SdkOption { publications.each { it.isSdk = true it.name = ProjectUtil.getProjectName(it.name) + if (isPublishMode) { + it.forceUseLocal(true) + } + it.online = this.isOnline + it.sdkNeedPublish = isPublishMode + it.impNeedPublish = isPublishMode Project childProject = ProjectUtil.getProject(project, it.name) if (childProject != null) { - PublicationUtil.initPublication(childProject, it) - PublicationManager.getInstance().addDependencyGraph(it.name, it) + if (isPublishMode) { + childProject.plugins.apply(Constants.PLUGIN_MAVEN_PUBLISH) + } + PublicationUtil.initPublication(childProject, it, isAutoVersion) + Logger.buildOutput("init publication:${it}") +// PublicationManager.getInstance().addDependencyGraph(it.name, it) Runtimes.addSdkPublication(childProject.name, it) + Runtimes.addImplPublication(childProject.name, it) + } + } + publications.each { + it.isSdk = true + it.name = ProjectUtil.getProjectName(it.name) + Project childProject = ProjectUtil.getProject(project, it.name) + if (childProject != null) { +// PublicationUtil.initPublication(childProject, it) + PublicationManager.getInstance().addDependencyGraph(it.name, it) +// Runtimes.addSdkPublication(childProject.name, it) } } } @@ -73,16 +143,16 @@ class SdkOption { String toString() { StringBuilder stringBuilder = new StringBuilder("\n") stringBuilder.append(" ------------------------------------------------------------------" + "\n") - stringBuilder.append(" | AndroidJarPath = " + Runtimes.getAndroidJarPath() + "\n" ) - stringBuilder.append(" | compileSdkVersion = " + compileSdkVersion + "\n" ) - stringBuilder.append(" | CompileOptions = " + compileOption.toString()+ "\n" ) - stringBuilder.append(" | configuration = [ " + "\n" ) + stringBuilder.append(" | AndroidJarPath = " + Runtimes.getAndroidJarPath() + "\n") + stringBuilder.append(" | compileSdkVersion = " + compileSdkVersion + "\n") + stringBuilder.append(" | CompileOptions = " + compileOption.toString() + "\n") + stringBuilder.append(" | configuration = [ " + "\n") Set keys = Runtimes.getSdkPublicationMap().keySet() - for(String key: keys){ + for (String key : keys) { PublicationOption publicationOption = Runtimes.getSdkPublication(key) - stringBuilder.append(" | name = " + publicationOption.name + ", gav = " + publicationOption.groupId + "." + publicationOption.artifactId + "\n" ) + stringBuilder.append(" | name = " + publicationOption.name + ", gav = " + publicationOption.groupId + "." + publicationOption.artifactId + "\n") } - stringBuilder.append(" | ] " + "\n" ) + stringBuilder.append(" | ] " + "\n") stringBuilder.append(" -------------------------------------------------------------------" + "\n") return stringBuilder.toString() } diff --git a/component-plugin/src/main/groovy/com/plugin/component/plugin/AbsPlugin.groovy b/component-plugin/src/main/groovy/com/plugin/component/plugin/AbsPlugin.groovy index 8bd7f8a..f6bcb11 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/plugin/AbsPlugin.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/plugin/AbsPlugin.groovy @@ -13,7 +13,7 @@ abstract class AbsPlugin { abstract void evaluateAfterAndroidPlugin(Project project) - abstract void afterEvaluateAfterAndroidPlugin(Project project) + abstract void afterEvaluateAfterAndroidPlugin(Project project,Project androidProject) abstract void afterAllEvaluate(Project root) } \ No newline at end of file diff --git a/component-plugin/src/main/groovy/com/plugin/component/plugin/BasePlugin.groovy b/component-plugin/src/main/groovy/com/plugin/component/plugin/BasePlugin.groovy index 495fb75..87cfc84 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/plugin/BasePlugin.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/plugin/BasePlugin.groovy @@ -28,7 +28,7 @@ class BasePlugin extends AbsPlugin { } @Override - void afterEvaluateAfterAndroidPlugin(Project project) { + void afterEvaluateAfterAndroidPlugin(Project project, Project androidProject) { } diff --git a/component-plugin/src/main/groovy/com/plugin/component/plugin/DebugPlugin.groovy b/component-plugin/src/main/groovy/com/plugin/component/plugin/DebugPlugin.groovy index 5a3b48d..5a7b5a5 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/plugin/DebugPlugin.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/plugin/DebugPlugin.groovy @@ -9,7 +9,7 @@ import org.gradle.api.Project class DebugPlugin extends BasePlugin { @Override - void afterEvaluateAfterAndroidPlugin(Project project) { + void afterEvaluateAfterAndroidPlugin(Project project, Project androidProject) { ProjectInfo projectInfo = Runtimes.getProjectInfo(project.name) if (projectInfo != null && ProjectUtil.isProjectSame(projectInfo.name, Runtimes.getDebugModuleName())) { ProjectUtil.modifyDebugSets(projectInfo.project.rootProject, projectInfo) diff --git a/component-plugin/src/main/groovy/com/plugin/component/plugin/PinPlugin.groovy b/component-plugin/src/main/groovy/com/plugin/component/plugin/PinPlugin.groovy index 0568909..59be691 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/plugin/PinPlugin.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/plugin/PinPlugin.groovy @@ -262,7 +262,7 @@ class PinPlugin extends BasePlugin { // generate microModules.xml for PinInfo IDEA plugin. def ideaFile = new File(buildResult.gradle.rootProject.rootDir, '.idea') if (!ideaFile.exists()) return - def pininfos = '\n\n' + def pininfos = '\n\n' Map allPins = Runtimes.getPinConfigurations() Set moduleNames = allPins.keySet() diff --git a/component-plugin/src/main/groovy/com/plugin/component/plugin/SdkPlugin.groovy b/component-plugin/src/main/groovy/com/plugin/component/plugin/SdkPlugin.groovy index 5d97b20..73e29e0 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/plugin/SdkPlugin.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/plugin/SdkPlugin.groovy @@ -1,23 +1,33 @@ package com.plugin.component.plugin -import com.plugin.component.log.Logger + +import com.plugin.component.ComponentPlugin import com.plugin.component.Runtimes import com.plugin.component.extension.PublicationManager import com.plugin.component.extension.module.ProjectInfo import com.plugin.component.extension.option.sdk.PublicationOption +import com.plugin.component.log.Logger import com.plugin.component.log.MutLineLog +import com.plugin.component.utils.AarUtil import com.plugin.component.utils.ProjectUtil import com.plugin.component.utils.PublicationUtil import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.Dependency +import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency +import org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency +import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection +import org.gradle.api.internal.file.collections.DefaultConfigurableFileTree class SdkPlugin extends BasePlugin { @Override void evaluateAfterAndroidPlugin(Project project) { + //针对每个module ProjectInfo projectInfo = Runtimes.getProjectInfo(project.name) //解析: component - //example :component(':library') + //example :将component(':library') 转换为 ':com.effective.android-librarySdk:' //规则: // 1. component 对象必须为实现 component 插件的project // 2. component() project 逻辑上被划分为 impl / debug / sdk,其中 sdk 通过 api 暴露给上层,impl 直接打包 @@ -29,58 +39,137 @@ class SdkPlugin extends BasePlugin { List publications = PublicationManager.getInstance().getPublicationByProject(project) project.dependencies { publications.each { + Logger.buildOutput("添加依赖名字: " + PublicationUtil.getPublication(it)) api PublicationUtil.getPublication(it) } } + Logger.buildOutput("Syncing gradle...") /** * Syncing Gradle will evaluateAfterAndroidPlugin the build files by comparing the current files to the project state that Gradle and Android Studio maintain. * If it finds any changes it will execute just those specific tasks. */ //project 需要依赖 sdk 中声明的依赖,意味着同步的时候,component.gradle 中 sdk { dependencies { }} 内容需要同步迁移到 project - if (projectInfo.isSync()) { - Logger.buildOutput("Syncing gradle...") - publications.each { - PublicationUtil.addPublicationDependencies(projectInfo, it) - } +// if (projectInfo.isSync()) { +// Logger.buildOutput("Syncing gradle...") + publications.each { + PublicationUtil.addPublicationDependencies(projectInfo, it) } +// } } @Override - void afterEvaluateAfterAndroidPlugin(Project project) { + void afterEvaluateAfterAndroidPlugin(Project project, Project androidProject) { + //整理资源路径 ProjectInfo projectInfo = Runtimes.getProjectInfo(project.name) ProjectUtil.modifySourceSets(projectInfo) - //todo 发布 -// List publicationList = PublicationManager.getInstance().getPublicationByProject(project) -// List publicationPublishList = new ArrayList<>() -// publicationList.each { -// if (it.version != null) { -// publicationPublishList.add(it) -// } -// } -// -// if (publicationPublishList.size() > 0) { -// project.plugins.apply(Constants.PLUGIN_MAVEN_PUBLISH) -// def publishing = project.extensions.getByName(Constants.PUBLISHING) -// if (mComponentExtension != null && mComponentExtension.configure != null) { -// publishing.repositories mComponentExtension.configure -// } -// -// publicationPublishList.each { -// PublicationUtil.createPublishTask(project, it) -// } -// } } @Override void afterAllEvaluate(Project root) { - ProjectInfo compileProject = Runtimes.getCompileProjectWhenAssemble() - if (compileProject != null) { +// handleImpAar(root) + List topSort = PublicationManager.getInstance().dependencyGraph.topSort() + Collections.reverse(topSort) + topSort.each { + PublicationOption publication = PublicationManager.getInstance().publicationDependencies.get(it) + if (publication == null) { + return + } + Project childProject = root.findProject(publication.project) + ProjectInfo projectInfo = Runtimes.getProjectInfo(childProject.name) + if (projectInfo.isAssemble) { + if (Runtimes.sSdkOption.isPublishMode) { + Runtimes.checkPublishEnable(childProject) + } + PublicationUtil.handleSdkPublish(childProject, ComponentPlugin.androidProject, publication, null) + handleImpAar(childProject, publication) + } else if (projectInfo.isPublish) { + publication.impNeedPublish = true + publication.sdkNeedPublish = true + def publishTask = null + if (projectInfo.isDebug) { + publishTask = root.tasks.named("ComponentPublishDebug") + } else { + publishTask = root.tasks.named("ComponentPublishRelease") + } + PublicationUtil.handleSdkPublish(childProject, ComponentPlugin.androidProject, publication, publishTask.get()) + PublicationUtil.handleImplPublish(childProject, publication, projectInfo.isDebug, publishTask.get()) + PublicationManager.getInstance().addImpPublication(publication) + } + } + dealAssemble(root) + } - MutLineLog mutLineLog = new MutLineLog() + private static void handleImpAar(Project project, PublicationOption publication) { + ProjectInfo projectInfo = Runtimes.getProjectInfo(project.name) + if (projectInfo == null) { + return + } + if (publication != null && !publication.invalid && ComponentPlugin.androidProject != null) { + boolean hasModify = PublicationManager.getInstance().hasImpModified(publication) + boolean hasGitDiff = PublicationManager.getInstance().hasModifyWithGitDiff(publication.impSourceSet) + File impFile = new File(Runtimes.sImplDir, PublicationUtil.getAarName(publication)) + if (!publication.forceLocal && publication.implVersion != null) { + long startTime = System.currentTimeMillis() + boolean isAarExists = AarUtil.isArrExits(project, publication, !projectInfo.isDebug) + long cost = System.currentTimeMillis() - startTime + if (isAarExists && !hasGitDiff) { + publication.impNeedPack = false + publication.useLocalImp = false + PublicationManager.getInstance().addImpPublication(publication) + Logger.buildOutput("Handle Maven Imp ${publication.name} cost ${cost}ms. aar exists.No need to pack") + } else if (!hasModify && impFile.exists()) { + publication.impNeedPack = false + publication.useLocalImp = true + PublicationManager.getInstance().addImpPublication(publication) + Logger.buildOutput("Handle Local Imp ${publication.name} cost ${cost}ms. aar not exists but not modify and local file exists.No need to pack") + } else { + publication.impNeedPack = true + publication.useLocalImp = true + if (projectInfo.isDebug) { + //注册打包arr task + AarUtil.packImpAar(project, ComponentPlugin.androidProject, publication, null) + } + Logger.buildOutput("Handle Local Imp ${publication.name} cost ${cost}ms. Need pack") + } + } else { + Logger.buildOutput("Handle Local Imp ${publication.name}.") + if (publication.impNeedPublish) { + boolean isAarExists = AarUtil.isArrExits(project, publication, !projectInfo.isDebug) + publication.useLocalImp = !isAarExists + publication.impNeedPack = hasModify || !isAarExists + if (!isAarExists) { + PublicationUtil.handleImplPublish(project, publication, projectInfo.isDebug, null) + } else { + publication.impNeedPublish = false + } + PublicationManager.getInstance().addImpPublication(publication) + } else { + if (projectInfo.isDebug) { + publication.useLocalImp = true + if (hasModify) { + publication.impNeedPack = true + //注册打包arr task + AarUtil.packImpAar(project, ComponentPlugin.androidProject, publication, null) + } else { + publication.impNeedPack = false + } + PublicationManager.getInstance().addImpPublication(publication) + } + } + } + } + } + + private void dealAssemble(Project root) { + //处理循环依赖 + ProjectInfo compileProject = Runtimes.getCompileProjectWhenAssemble() + MutLineLog mutLineLog = new MutLineLog() + mutLineLog.build4("开始处理Assemble场景 compileProject:${compileProject}") + if (compileProject != null) { if (Runtimes.sAssembleModules.size() > 1) { mutLineLog.build4("task has one more assemble module,skip transforming [component] to [project]") } else { @@ -108,21 +197,181 @@ class SdkPlugin extends BasePlugin { currentDependencies.addAll(nextDependencies) nextDependencies.clear() } - + hasResolve.each { + Logger.buildOutput("resolve project:${compileProject.name} realDependency:${it}") + } if (!hasResolve.isEmpty()) { - StringBuilder stringBuilder = new StringBuilder() - for (String realDependency : hasResolve) { - stringBuilder.append(" :project(") - stringBuilder.append(realDependency) - stringBuilder.append(")") - compileProject.project.dependencies { - implementation compileProject.project.project(":" + realDependency) - } - } - mutLineLog.build4("application[" + compileProject.name + "] component 合并依赖 = " + stringBuilder.toString()) + handleCompileModuleDependency(compileProject, root, hasResolve, mutLineLog) } } - Logger.buildBlockLog("单 ASSEMBLE 场景处理循环依赖", mutLineLog) } + Logger.buildBlockLog("单 ASSEMBLE 场景处理循环依赖", mutLineLog) + } + + private void handleCompileModuleDependency(ProjectInfo compileProject, Project root, HashSet hasResolve, MutLineLog mutLineLog) { + StringBuilder stringBuilder = new StringBuilder() + //收集所有模块的引用配置 + ArrayList allConfigList = new ArrayList<>() + compileProject.project.rootProject.allprojects.each { project -> + if (project == root) return + if (!Runtimes.shouldApplyComponentPlugin(project)) return + project.configurations.each { config -> + if (config.name == "api" || config.name == "implementation" + || config.name == "compileOnly" + || config.name == "debugImplementation" + || config.name == "debugApi" + || config.name == "debugCompileOnly") { + allConfigList.add(config) + } + } + } + //收集project之间的引用依赖关系 收集被依赖的project,需要参与编译 + HashMap> needCompileProjectConfigMap = new HashMap<>() + compileProject.project.rootProject.allprojects.each { project -> + if (project == root) return + allConfigList.each { config -> + for (Dependency dependency : config.dependencies) { + if (dependency instanceof DefaultProjectDependency && dependency.dependencyProject.name == project.name) { + ArrayList configs = needCompileProjectConfigMap.get(project) + if (configs == null) { + configs = new ArrayList<>() + needCompileProjectConfigMap.put(project, configs) + } + configs.add(config) + //一个config只需要添加一次就好了 + return + } + } + } + } + //剔除被收集的编译工程的引用 + allConfigList.clear() + compileProject.project.rootProject.allprojects.each { project -> + if (project == root) return + if (!Runtimes.shouldApplyComponentPlugin(project)) return + project.configurations.each { config -> + if (config.name == "api" || config.name == "implementation" + || config.name == "compileOnly" + || config.name == "debugImplementation" + || config.name == "debugApi" + || config.name == "debugCompileOnly") { + if (compileProject.project != project) + allConfigList.add(config) + } + } + } + HashMap> aarList = new HashMap<>() + for (String realDependency : hasResolve) { + PublicationOption publication = Runtimes.getImplPublication(realDependency) + Logger.buildOutput("resolve publication ${publication}") + if (publication != null && compileProject.isDebug) { + if (publication.impNeedPack) { + stringBuilder.append(" :project(") + stringBuilder.append(realDependency) + stringBuilder.append(")") + compileProject.project.dependencies { + implementation compileProject.project.project(":" + realDependency) + } + needCompileProjectConfigMap.put(Runtimes.getProjectInfo(realDependency).project, new ArrayList()) + } else { + Map map = new LinkedHashMap<>() + if (publication.useLocalImp) { + stringBuilder.append("${PublicationUtil.getAarName(publication)} ") + map.put("name", publication.groupId + '-' + publication.artifactId + '-imp') + map.put("ext", "aar") + } else { + stringBuilder.append("${PublicationUtil.getImpMavenGAV(publication, !compileProject.isDebug)} ") + map.put("path", PublicationUtil.getImpMavenGAV(publication, !compileProject.isDebug)) + } + aarList.put(realDependency, map) + } + PublicationManager.getInstance().addImpPublication(publication) + } else { + stringBuilder.append(" :project(") + stringBuilder.append(realDependency) + stringBuilder.append(")") + compileProject.project.dependencies { + implementation compileProject.project.project(":" + realDependency) + } + needCompileProjectConfigMap.put(Runtimes.getProjectInfo(realDependency).project, new ArrayList()) + } + } + if (compileProject.isDebug && !aarList.isEmpty()) { + compileProject.project.rootProject.allprojects.each { project -> + if (project == root) return + Logger.buildOutput("replace aar:project: $project.name") + // 替换成aar文件 + aarList.each { map -> + //剔除所有配置中的不需要编译模块的依赖 + project.configurations.each { config -> + config.dependencies.removeAll { dependency -> + dependency instanceof DefaultProjectDependency && dependency.dependencyProject.name == map.key + } + } + project.dependencies { + if (map.getValue().size() > 1) { + api map.value + } else { + api map.value.get("path") + } + } + } + } + aarList.each { map -> + allConfigList.each { config -> + config.dependencies.removeAll { dependency -> + dependency instanceof DefaultProjectDependency && dependency.dependencyProject.name == map.key + } + } + } + List> needAndDependency = new ArrayList<>() + //分离所有dependency 进行预处理 + allConfigList.each { config -> + config.dependencies.each { dependency -> + if (dependency instanceof DefaultProjectDependency) { + def dependencyClone = dependency.copy() + dependencyClone.targetConfiguration = null + Map map = new HashMap<>() + map.put(config.name, dependencyClone) + needAndDependency.add(map) + } else { + if (dependency instanceof DefaultSelfResolvingDependency && (dependency.files instanceof DefaultConfigurableFileCollection || dependency.files instanceof DefaultConfigurableFileTree)) { + // 这里的依赖是以下两种: 无需添加在 编译project ,因为 jar 包直接进入 自身的 aar 中的libs 文件夹 + // implementation rootProject.files("libs/*.jar") + // implementation fileTree(dir: "libs", include: ["*.jar"]) + + } else { + Map map = new HashMap<>() + map.put(config.name, dependency) + needAndDependency.add(map) + } + } + + } + } + //遍历所有需要编译的project 将引用传递至需要编译的project中,module引用只需要传递api方式引用的 + needCompileProjectConfigMap.each { entry -> + if (entry.key == root) return + needAndDependency.each { map -> + Map.Entry config = map.entrySet().first() + Dependency dependency = config.value + if (config.key == "api") { + if (dependency instanceof DefaultProjectDependency && dependency.dependencyProject.name == entry.key.name) { + return + } + compileProject.project.dependencies.add(config.key, config.value) + entry.key.dependencies.add(config.key, config.value) + } else { + //不传递implement project + if (dependency instanceof DefaultProjectDependency) { + return + } + compileProject.project.dependencies.add(config.key, config.value) +// entry.key.dependencies.add(config.key, config.value) + } + } + } + } + mutLineLog.build4("application[" + compileProject.name + "] component 合并依赖 = " + stringBuilder.toString()) } } diff --git a/component-plugin/src/main/groovy/com/plugin/component/task/AarLocalPublishTask.groovy b/component-plugin/src/main/groovy/com/plugin/component/task/AarLocalPublishTask.groovy new file mode 100644 index 0000000..2e6460c --- /dev/null +++ b/component-plugin/src/main/groovy/com/plugin/component/task/AarLocalPublishTask.groovy @@ -0,0 +1,43 @@ +package com.plugin.component.task + +import com.plugin.component.Runtimes +import com.plugin.component.extension.option.sdk.PublicationOption +import com.plugin.component.log.Logger +import com.plugin.component.utils.FileUtil +import com.plugin.component.utils.PublicationUtil +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.TaskAction + +/** + * 复制aar到Runtime#sImplDir路径下 + */ +class AarLocalPublishTask extends DefaultTask { + + @Internal + String inputPath + @Internal + File inputFile + + @Internal + File outputDir + @Internal + PublicationOption publication + + @TaskAction + void uploadLocalMaven() { + String aarName = PublicationUtil.getAarName(publication) + inputPath = FileUtil.findAarPath(project) + outputDir = Runtimes.sImplDir + if (inputPath != null) { + inputFile = new File(inputPath) + File outputFile = new File(outputDir, aarName) + if (outputFile.exists()) { + outputFile.delete() + } + FileUtil.copyFile(inputFile, outputFile) + } else { + Logger.buildOutput("project:${project.name} aar not found") + } + } +} \ No newline at end of file diff --git a/component-plugin/src/main/groovy/com/plugin/component/task/CompileSdkTask.groovy b/component-plugin/src/main/groovy/com/plugin/component/task/CompileSdkTask.groovy index 7147696..d24878f 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/task/CompileSdkTask.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/task/CompileSdkTask.groovy @@ -4,6 +4,8 @@ package com.plugin.component.task import com.plugin.component.Runtimes import com.plugin.component.extension.PublicationManager import com.plugin.component.extension.option.sdk.PublicationOption +import com.plugin.component.utils.JarUtil +import com.plugin.component.utils.PublicationUtil import org.gradle.api.DefaultTask import org.gradle.api.tasks.TaskAction @@ -18,14 +20,17 @@ class CompileSdkTask extends DefaultTask { @TaskAction void compileSource() { def project = getProject() - def releaseJar = JarUtil.packJavaSourceJar(project, publication, Runtimes.getAndroidJarPath(), Runtimes.getCompileOption(), false) - if (releaseJar == null) { - throw new RuntimeException("nothing to push.") + File target = new File(Runtimes.sSdkDir, PublicationUtil.getJarName(publication)) + if (!target.exists()) { + def releaseJar = JarUtil.packJavaSourceJar(project, publication, Runtimes.getAndroidJarPath(), Runtimes.getCompileOption(), false) + if (releaseJar == null) { + throw new RuntimeException("nothing to push.") + } } JarUtil.packJavaDocSourceJar(publication) PublicationManager publicationManager = PublicationManager.getInstance() if (publication.versionNew != null) { - publication.version = publication.versionNew + publication.sdkVersion = publication.versionNew } publicationManager.addPublication(publication) } diff --git a/component-plugin/src/main/groovy/com/plugin/component/task/ComponentPublishTask.groovy b/component-plugin/src/main/groovy/com/plugin/component/task/ComponentPublishTask.groovy new file mode 100644 index 0000000..28c598d --- /dev/null +++ b/component-plugin/src/main/groovy/com/plugin/component/task/ComponentPublishTask.groovy @@ -0,0 +1,24 @@ +package com.plugin.component.task + +import com.plugin.component.Runtimes +import com.plugin.component.log.Logger +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.TaskAction + +/** + * 组件发布 + * create by linyue 2022/08/22 + */ +class ComponentPublishTask extends DefaultTask { + + ComponentPublishTask() { + setGroup("component plugin") + } + + @TaskAction + void publish() { + Logger.buildOutput("startPublish") + Runtimes.checkPublishEnable(project) + } + +} \ No newline at end of file diff --git a/component-plugin/src/main/groovy/com/plugin/component/transform/ComponentTransform.java b/component-plugin/src/main/groovy/com/plugin/component/transform/ComponentTransform.java index dcf6d0e..c2e4d14 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/transform/ComponentTransform.java +++ b/component-plugin/src/main/groovy/com/plugin/component/transform/ComponentTransform.java @@ -184,7 +184,7 @@ public class ComponentTransform extends Transform { ScanSummaryInfo cacheSummary = readPluginCache(); // 读取缓存 - ScanSummaryInfo scanSummaryInfo = ScanRuntime.updateSummaryInfo(cacheSummary); // 整理本次扫码结果,返回最新的模块结构 + ScanSummaryInfo scanSummaryInfo = ScanRuntime.updateSummaryInfo(cacheSummary); // 整理本次扫描结果,返回最新的模块结构 ScanRuntime.buildComponentSdkInfo(); ScanRuntime.logScanInfo(); diff --git a/component-plugin/src/main/groovy/com/plugin/component/transform/ComponentWeaver.java b/component-plugin/src/main/groovy/com/plugin/component/transform/ComponentWeaver.java index dc194c7..f34db1f 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/transform/ComponentWeaver.java +++ b/component-plugin/src/main/groovy/com/plugin/component/transform/ComponentWeaver.java @@ -51,7 +51,6 @@ public class ComponentWeaver implements IWeaver { String inputPath = inputJar.getAbsolutePath(); String outputPath = outputJar.getAbsolutePath(); ZipFile inputZip = new ZipFile(inputJar); - ZipOutputStream outputZip = new ZipOutputStream(new BufferedOutputStream(java.nio.file.Files.newOutputStream(outputJar.toPath()))); Enumeration inEntries = inputZip.entries(); while (inEntries.hasMoreElements()) { ZipEntry entry = inEntries.nextElement(); @@ -63,26 +62,11 @@ public class ComponentWeaver implements IWeaver { beforeWeaveClass(inputPath, outputPath, className); - if (!isWeavableClass(className)) { - newEntryContent = org.apache.commons.io.IOUtils.toByteArray(originalFile); - } else { - newEntryContent = weaveSingleClassToByteArray(inputPath, originalFile); + if (isWeavableClass(className)) { + weaveSingleClassToByteArray(inputPath, originalFile); } - CRC32 crc32 = new CRC32(); - crc32.update(newEntryContent); - outEntry.setCrc(crc32.getValue()); - outEntry.setMethod(ZipEntry.STORED); - outEntry.setSize(newEntryContent.length); - outEntry.setCompressedSize(newEntryContent.length); - outEntry.setLastAccessTime(ZERO); - outEntry.setLastModifiedTime(ZERO); - outEntry.setCreationTime(ZERO); - outputZip.putNextEntry(outEntry); - outputZip.write(newEntryContent); - outputZip.closeEntry(); } - outputZip.flush(); - outputZip.close(); + FileUtils.copyFile(inputJar, outputJar); } public final void weaveSingleClassToFile(File inputFile, File outputFile, String inputBaseDir) throws IOException { @@ -98,10 +82,8 @@ public class ComponentWeaver implements IWeaver { FileUtils.touch(outputFile); InputStream inputStream = new FileInputStream(inputFile); byte[] bytes = weaveSingleClassToByteArray(inputPath, inputStream); - FileOutputStream fos = new FileOutputStream(outputFile); - fos.write(bytes); - fos.close(); inputStream.close(); + FileUtils.copyFile(inputFile, outputFile); } else { if (inputFile.isFile()) { FileUtils.touch(outputFile); diff --git a/component-plugin/src/main/groovy/com/plugin/component/transform/ScanCodeAdapter.groovy b/component-plugin/src/main/groovy/com/plugin/component/transform/ScanCodeAdapter.groovy index 2fc9486..ad2bd1e 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/transform/ScanCodeAdapter.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/transform/ScanCodeAdapter.groovy @@ -10,6 +10,9 @@ import org.objectweb.asm.ClassVisitor import org.objectweb.asm.Opcodes import org.objectweb.asm.Type +/** + * 扫描注解并且收集 + */ class ScanCodeAdapter extends ClassVisitor { private String className diff --git a/component-plugin/src/main/groovy/com/plugin/component/utils/AarUtil.groovy b/component-plugin/src/main/groovy/com/plugin/component/utils/AarUtil.groovy new file mode 100644 index 0000000..242fd82 --- /dev/null +++ b/component-plugin/src/main/groovy/com/plugin/component/utils/AarUtil.groovy @@ -0,0 +1,136 @@ +package com.plugin.component.utils + +import com.android.build.gradle.AppExtension +import com.android.build.gradle.api.ApplicationVariant +import com.plugin.component.Runtimes +import com.plugin.component.extension.PublicationManager +import com.plugin.component.extension.option.sdk.PublicationOption +import com.plugin.component.log.Logger +import com.plugin.component.task.AarLocalPublishTask +import com.plugin.component.task.ComponentPublishTask +import org.apache.http.util.TextUtils +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.dsl.RepositoryHandler +import org.gradle.api.tasks.TaskProvider + +class AarUtil { + + static void packImpAar(Project currentProject, Project androidProject, PublicationOption publication, Task dependTask) { + AppExtension appExtension = androidProject.extensions.getByType(AppExtension.class) + appExtension.applicationVariants.each { + if (it.buildType.name.toLowerCase().contains("debug")) { + if (dependTask == null) { + TaskProvider assembleTask = getAssembleTask(androidProject, it) + Logger.buildOutput("getAssembleTask:${assembleTask.name}") + dependTask = assembleTask.get() + } + hookBundleAarTask(currentProject, dependTask, it.buildType.name, publication, false) + } + } + } + + static void packImpAarRelease(Project currentProject, Project androidProject, PublicationOption publication, Task dependTask) { + AppExtension appExtension = androidProject.extensions.getByType(AppExtension.class) + appExtension.applicationVariants.each { + if (it.buildType.name.toLowerCase().contains("release")) { + if (dependTask == null) { + TaskProvider assembleTask = getAssembleTask(androidProject, it) + Logger.buildOutput("getAssembleTask:${assembleTask.name}") + dependTask = assembleTask.get() + } + hookBundleAarTask(currentProject, dependTask, it.buildType.name, publication, true) + } + } + } + + static TaskProvider getAssembleTask(Project androidProject, ApplicationVariant variant) { + TaskProvider assembleTask = + androidProject.tasks.named("assemble${variant.flavorName.capitalize()}${variant.buildType.name.capitalize()}") + return assembleTask + } + + static boolean isArrExits(Project project, PublicationOption publication, boolean isRelease) { + String filePath = null + String fileName = isRelease ? publication.getArtifactIdString() + "-imp-" + publication.implVersion + ".aar" : publication.getArtifactIdString() + "-imp-debug-" + publication.implVersion + ".aar" + String artifactId = isRelease ? publication.getArtifactIdString() + "-imp" : publication.getArtifactIdString() + "-imp-debug" + // http://172.16.xxx.xxx:8081/nexus/content/groups/public/com/xxx/cif/xxx-cif-api/0.0.1-SNAPSHOT/xxx-cif-api-0.0.1-20170515.040917-89.jar + String url = Runtimes.sSdkOption.getMavenUrl() + String result = HttpUrlConnectHelper.sendRequest("$url/${publication.groupId.replace('.', '/')}/$artifactId/${publication.implVersion}/$fileName","HEAD") + if (!TextUtils.isEmpty(result)) { + filePath = "$url/${publication.groupId.replace('.', '/')}/$artifactId/${publication.implVersion}/$fileName" + } +// def name = "component[${publication.groupId}-${publication.artifactId}]Classpath" +// Configuration configuration = project.configurations.create(name) +// project.dependencies.add(name, PublicationUtil.getImpMavenGAV(publication, isRelease)) +// try { +// configuration.copy().files.each { +// Logger.buildOutput("aarName:${it.name}") +// if (it.name.endsWith(fileName)) { +// filePath = it.absolutePath +// } +// } +// } catch (Exception e) { +//// e.printStackTrace() +// Logger.buildOutput(e.getMessage()) +// } +// project.configurations.remove(configuration) + return filePath != null + } + + private static void hookBundleAarTask(Project currentProject, Task assembleTask, String buildType, PublicationOption publication, boolean isRelease) { + Task bundleTask = assembleTask + if (publication.impNeedPack) { + bundleTask = getBundleAarTask(currentProject, buildType.capitalize()) + bundleTask.getTaskDependencies().getDependencies(bundleTask).each { + if (it != assembleTask && assembleTask instanceof ComponentPublishTask) { + //找到bundleAar的所有task,让他们都依赖ComponentPublishTask + it.dependsOn(assembleTask) + } + } + bundleTask.dependsOn(assembleTask) + bundleTask.mustRunAfter(assembleTask) + assembleTask.finalizedBy(bundleTask) + } + AarLocalPublishTask aarLocalPublishTask = currentProject.tasks.maybeCreate("uploadLocalMaven" + buildType.capitalize(), AarLocalPublishTask.class) + aarLocalPublishTask.publication = publication + aarLocalPublishTask.dependsOn(bundleTask) + bundleTask.finalizedBy(aarLocalPublishTask) + aarLocalPublishTask.doFirst { + Logger.buildOutput("${aarLocalPublishTask.name} start") + } + aarLocalPublishTask.doLast { + Logger.buildOutput("${aarLocalPublishTask.name} finish") + } + + if (publication.impNeedPublish) { + def publicationName = 'Component-' + currentProject.name + buildType.capitalize() + 'Imp' + String publishTaskNamePrefix = "publish${publicationName}PublicationTo" + currentProject.tasks.whenTaskAdded { + if (it.name.startsWith(publishTaskNamePrefix)) { + it.dependsOn aarLocalPublishTask + it.doLast { + PublicationManager.getInstance().addImpPublication(publication) + } + } + } + + PublicationUtil.createImpPublishingPublication(currentProject, publication, publicationName, isRelease) + TaskProvider publishTask = currentProject.tasks.named("${publishTaskNamePrefix}MavenRepository") + aarLocalPublishTask.finalizedBy(publishTask.get()) + } + } + + /** + * 获取到每个模块的bundleXXXAar task,用于打包每个module的aar + * @param currentProject + * @param variantName + */ + private static Task getBundleAarTask(Project currentProject, String variantName) { + String taskName = "bundle${variantName}Aar" + TaskProvider bundleAarTask = currentProject.tasks.named(taskName) + return bundleAarTask.get() + } + +} \ No newline at end of file diff --git a/component-plugin/src/main/groovy/com/plugin/component/utils/FileUtil.groovy b/component-plugin/src/main/groovy/com/plugin/component/utils/FileUtil.groovy index e735789..24abce9 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/utils/FileUtil.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/utils/FileUtil.groovy @@ -1,6 +1,8 @@ package com.plugin.component.utils +import com.plugin.component.ComponentPlugin import com.plugin.component.Constants +import org.gradle.api.Project class FileUtil { @@ -62,4 +64,30 @@ class FileUtil { e.printStackTrace() } } + + static String findAarPath(Project project) { + File aarDir = new File(project.buildDir.absolutePath + "/outputs/aar/") + if (aarDir.exists()) { + File[] files = aarDir.listFiles(new FilenameFilter() { + @Override + boolean accept(File dir, String name) { + return name.endsWith(".aar") + } + }) + if (files.size() > 0) { + return files.first().absolutePath + } + } + return null + } + + static String shell(String cmd) { + def out = new ByteArrayOutputStream() + ComponentPlugin.rootProject.exec { + executable 'bash' + args '-c', cmd + standardOutput = out + } + return out.toString().trim() + } } diff --git a/component-plugin/src/main/groovy/com/plugin/component/utils/GitUtil.groovy b/component-plugin/src/main/groovy/com/plugin/component/utils/GitUtil.groovy new file mode 100644 index 0000000..c1d1b6e --- /dev/null +++ b/component-plugin/src/main/groovy/com/plugin/component/utils/GitUtil.groovy @@ -0,0 +1,32 @@ +package com.plugin.component.utils + +class GitUtil { + + static boolean canPublish(String filePath) { + if (isGitExist()) { + if (getGitBranch().matches("(^develop\$)|(^release/(.)*)")) { + return getGitDiff(filePath).isEmpty() + } + return getGitDiff(filePath).isEmpty() && getGitCherry().isEmpty() + } else { + return true + } + } + + static boolean isGitExist() { + return !FileUtil.shell("git --help").contains("git command not found") + } + + static String getGitDiff(String filePath) { + return FileUtil.shell("git diff ${filePath}") + } + + static String getGitCherry() { + return FileUtil.shell("git cherry") + } + + static String getGitBranch() { + return FileUtil.shell("git symbolic-ref --short -q HEAD") + } + +} \ No newline at end of file diff --git a/component-plugin/src/main/groovy/com/plugin/component/utils/HttpUrlConnectHelper.java b/component-plugin/src/main/groovy/com/plugin/component/utils/HttpUrlConnectHelper.java new file mode 100644 index 0000000..3808661 --- /dev/null +++ b/component-plugin/src/main/groovy/com/plugin/component/utils/HttpUrlConnectHelper.java @@ -0,0 +1,62 @@ +package com.plugin.component.utils; + +import com.plugin.component.log.Logger; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +/** + * createBy:linyue + * date:2022/9/9 + * description: + */ +public class HttpUrlConnectHelper { + public static String sendRequest(String urlParam, String requestType) { + + HttpURLConnection con = null; + + BufferedReader buffer = null; + StringBuffer resultBuffer = null; + + try { + URL url = new URL(urlParam); + //得到连接对象 + con = (HttpURLConnection) url.openConnection(); + //设置请求类型 + con.setRequestMethod(requestType); + //设置请求需要返回的数据类型和字符集类型 + con.setRequestProperty("Content-Type", "application/json;charset=utf-8"); + //允许写出 + con.setDoOutput(true); + //允许读入 + con.setDoInput(true); + //不使用缓存 + con.setUseCaches(false); + //得到响应码 + int responseCode = con.getResponseCode(); +// Logger.buildOutput("responseCode", responseCode); + if (responseCode == HttpURLConnection.HTTP_OK) { + if (requestType.equals("HEAD")) { + return "OK"; + } + //得到响应流 + InputStream inputStream = con.getInputStream(); + //将响应流转换成字符串 + resultBuffer = new StringBuffer(); + String line; + buffer = new BufferedReader(new InputStreamReader(inputStream)); + while ((line = buffer.readLine()) != null) { + resultBuffer.append(line); + } + return resultBuffer.toString(); + } + + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } +} diff --git a/component-plugin/src/main/groovy/com/plugin/component/utils/JarUtil.groovy b/component-plugin/src/main/groovy/com/plugin/component/utils/JarUtil.groovy index e64e39b..04474b4 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/utils/JarUtil.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/utils/JarUtil.groovy @@ -1,11 +1,13 @@ package com.plugin.component.utils import com.plugin.component.Constants +import com.plugin.component.extension.option.sdk.PublicationDependencyModuleOption import com.plugin.component.log.Logger import com.plugin.component.Runtimes import com.plugin.component.extension.PublicationManager import com.plugin.component.extension.option.sdk.CompileOptions import com.plugin.component.extension.option.sdk.PublicationOption +import org.apache.http.util.TextUtils import org.gradle.api.GradleException import org.gradle.api.JavaVersion import org.gradle.api.Project @@ -14,7 +16,6 @@ import org.gradle.internal.jvm.Jvm import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler -import java.util.concurrent.TimeUnit import java.util.jar.JarEntry import java.util.jar.JarFile import java.util.zip.ZipFile @@ -45,8 +46,8 @@ class JarUtil { outputDir.mkdirs() def argFiles = [] - File file = new File(publication.misSourceSet.path) - String prefix = publication.misSourceSet.path + File file = new File(publication.sdkSourceSet.path) + String prefix = publication.sdkSourceSet.path filterJavaSource(file, prefix, sourceDir, argFiles, publication.sourceFilter) if (argFiles.size() == 0) { return null @@ -57,21 +58,21 @@ class JarUtil { Configuration configuration = project.configurations.create(name) if (publication.dependencies != null) { if (publication.dependencies.implementation != null) { - publication.dependencies.implementation.each { - if (it instanceof String && it.startsWith(Constants.COMPONENT_PRE)) { - project.dependencies.add(name, PublicationUtil.parseComponent(it)) - }else{ - project.dependencies.add(name, it) + publication.dependencies.implementation.each { dependency -> +// Logger.buildOutput("packJar: publication:${PublicationUtil.getPublicationId(publication)} move dependencies:${it}") + if (dependency instanceof PublicationDependencyModuleOption) { + project.dependencies.add(name, dependency.path) + } else { + project.dependencies.add(name, dependency) } - } } if (publication.dependencies.compileOnly != null) { - publication.dependencies.compileOnly.each { - if (it instanceof String && it.startsWith(Constants.COMPONENT_PRE)) { - project.dependencies.add(name, PublicationUtil.parseComponent(it)) - }else{ - project.dependencies.add(name, it) + publication.dependencies.compileOnly.each { dependency -> + if (dependency instanceof PublicationDependencyModuleOption) { + project.dependencies.add(name, dependency.path) + } else { + project.dependencies.add(name, dependency) } } } @@ -79,6 +80,8 @@ class JarUtil { def classPath = [androidJarPath] configuration.copy().files.each { +// Logger.buildOutput("packJar: copyFile name:${it.name}") + //添加所有打包的classpatch if (it.name.endsWith('.aar')) { classPath << getAARClassesJar(it) } else { @@ -86,7 +89,7 @@ class JarUtil { } } project.configurations.remove(configuration) - + Logger.buildOutput("packing ${project.name} sdk jar") return generateJavaSourceJar(classesDir, argFiles, classPath, compileOptions, vars) } @@ -106,12 +109,6 @@ class JarUtil { CompileOptions compileOptions, boolean vars) { - //window classpath 路径分割符号与 mac/linux需要做区分 - def classpathSeparator = ";" - if (!System.properties['os.name'].toLowerCase().contains('windows')) { - classpathSeparator = ":" - } - boolean keepParameters = vars && Jvm.current().javaVersion >= JavaVersion.VERSION_1_8 //读取java和kotlin文件 @@ -140,7 +137,7 @@ class JarUtil { JavaVersion targetCompatibility = compileOptions.targetCompatibility def target = targetCompatibility.toString() if (!targetCompatibility.isJava8() && !targetCompatibility.isJava6()) { - throw new GradleException("Failure to compile component kotlin source to bytecode: unknown JVM target version: $target, supported versions: 1.6, 1.8\nTry:\n " + + throw new GradleException("Failure to compile component kotlin source to bytecode: unknown JVM target sdkVersion: $target, supported versions: 1.6, 1.8\nTry:\n " + " module {\n" + " ...\n" + " compileOption {\n" + @@ -156,10 +153,12 @@ class JarUtil { if (classPath.size() > 0) { args.add('-classpath') - args.add(classPath.join(classpathSeparator)) + args.add(classPath.join(File.pathSeparator)) } ExitCode exitCode = compiler.exec(System.out, (String[]) args.toArray()) + Logger.buildOutput("编译参数: " + (String[]) args.toArray()) + Logger.buildOutput("kotlin 编译结果: +${exitCode}") if (exitCode != ExitCode.OK) { throw new GradleException("Failure to compile component kotlin source to bytecode.") } @@ -170,25 +169,67 @@ class JarUtil { } if (!javaFiles.isEmpty()) { - def command = "javac " + (keepParameters ? "-parameters" : "") + " -d . -encoding UTF-8 -target " + compileOptions.targetCompatibility.toString() + " -source " + compileOptions.sourceCompatibility.toString() + (classPath.size() > 0 ? (" -classpath " + classPath.join(classpathSeparator) + " ") : "") + javaFiles.join(' ') - def p = (command).execute(null, classesDir) + LinkedList paras = new LinkedList(); + paras.add('javac') + paras.add('-parameters') + paras.add('-d') + paras.add(classesDir.getAbsolutePath()) + paras.add('-encoding') + paras.add('UTF-8') + paras.add('-target') + paras.add(compileOptions.targetCompatibility.toString()) + paras.add('-source') + paras.add(compileOptions.sourceCompatibility.toString()) - def result = p.waitFor(30, TimeUnit.SECONDS) - if (!result) { - throw new GradleException("Timed out when compile component java source to bytecode with command.\nExecute command:\n" + command) - } + paras.add('-classpath') + paras.add(classPath.join(File.pathSeparator)) + paras.addAll(javaFiles); - if (p.exitValue() != 0) { - throw new GradleException("Failure to compile component java source to bytecode: \n" + p.err.text + "\nExecute command:\n" + command) + String[] javacParameters = (String[]) paras.toArray(new String[paras.size()]) + + Runtime runtime = Runtime.getRuntime() + def p = runtime.exec(javacParameters, null, classesDir); + Logger.buildOutput("javac execute command:${javacParameters}") + def shellErrorResultReader = new BufferedReader(new InputStreamReader(p.getErrorStream())); + def shellInfoResultReader = new BufferedReader(new InputStreamReader(p.getInputStream())); + //需要输出缓冲区数据 不然会阻塞在waitFor + String infoLine; + while ((infoLine = shellInfoResultReader.readLine()) != null) { + Logger.buildOutput("脚本文件执行信息:{}", infoLine); } + String errorLine; + while ((errorLine = shellErrorResultReader.readLine()) != null) { + Logger.buildOutput("脚本文件执行信息:{}", errorLine); + } + def result = p.waitFor() + Logger.buildOutput("javac execute result:${result}") + if (result != 0) { + throw new GradleException("Failure to compile component java source to bytecode: \n" + p.err.text + "\nExecute command:\n" + javacParameters) + } + p.destroy() + } + Logger.buildOutput("jar execute command: jar cf outputs/classes.jar -C classes . \nclasses path ${classesDir.parentFile.absolutePath}") +// def p = "jar cvf outputs/classes.jar -C classes . ".execute(null, classesDir.parentFile) + Runtime runtime = Runtime.getRuntime() + def p = runtime.exec("jar cf outputs/classes.jar -C classes . ", null, classesDir.parentFile); + def shellErrorResultReader = new BufferedReader(new InputStreamReader(p.getErrorStream())); + def shellInfoResultReader = new BufferedReader(new InputStreamReader(p.getInputStream())); + //需要输出缓冲区数据 不然会阻塞在waitFor + String infoLine; + while ((infoLine = shellInfoResultReader.readLine()) != null) { + Logger.buildOutput("脚本文件执行信息:{}", infoLine); + } + String errorLine; + while ((errorLine = shellErrorResultReader.readLine()) != null) { + Logger.buildOutput("脚本文件执行信息:{}", errorLine); } - - def p = "jar cvf outputs/classes.jar -C classes . ".execute(null, classesDir.parentFile) def result = p.waitFor() - p.waitFor() + Logger.buildOutput("jar excute result:${result}") +// p.waitFor() if (result != 0) { throw new RuntimeException("failure to package classes.jar: \n" + p.err.text) } + p.destroy() return new File(classesDir.parentFile, 'outputs/classes.jar') } @@ -201,13 +242,25 @@ class JarUtil { def javaSource = new File(publication.buildDir, "javaSource") javaSource.deleteDir() javaSource.mkdirs() - filterJavaDocSource(new File(publication.misSourceSet.path), publication.misSourceSet.path, javaSource) + def outputDir = new File(publication.buildDir, Constants.BUILD_OUTPUT_DIR) + if (!outputDir.exists()) { + outputDir.mkdirs() + } + filterJavaDocSource(new File(publication.sdkSourceSet.path), publication.sdkSourceSet.path, javaSource) return generateJavaDocSourceJar(javaSource) } private static File generateJavaDocSourceJar(File sourceDir) { def p = "jar cvf ../outputs/classes-source.jar .".execute(null, sourceDir) + + def shellInfoResultReader = new BufferedReader(new InputStreamReader(p.getInputStream())); + //需要输出缓冲区数据 不然会阻塞在waitFor + String infoLine; + while ((infoLine = shellInfoResultReader.readLine()) != null) { + Logger.buildOutput("脚本文件执行信息:{}", infoLine); + } def result = p.waitFor() + Logger.buildOutput("jar excute result:${result}") if (result != 0) { throw new RuntimeException("failure to make component-sdk java source directory: \n" + p.err.text) } @@ -217,6 +270,7 @@ class JarUtil { /** + * 递归过滤目录,找到文件copy到目标输出路径 * * @param file * @param prefix @@ -264,16 +318,67 @@ class JarUtil { } } + static boolean isMavenJarExists(Project project, PublicationOption publication) { + String filePath = null + String fileName = publication.artifactId + "-" + publication.sdkVersion + ".jar" +// http://172.16.xxx.xxx:8081/nexus/content/groups/public/com/xxx/cif/xxx-cif-api/0.0.1-SNAPSHOT/xxx-cif-api-0.0.1-20170515.040917-89.jar + String url = Runtimes.sSdkOption.getMavenUrl() + String line = HttpUrlConnectHelper.sendRequest("$url/${publication.groupId.replace('.', '/')}/${publication.artifactId}/${publication.sdkVersion}/$fileName", "HEAD") + if (!TextUtils.isEmpty(line)) { + filePath = "$url/${publication.groupId.replace('.', '/')}/${publication.artifactId}/${publication.sdkVersion}/$fileName" + } +// def name = "component[${publication.groupId}-${publication.artifactId}]Classpath" +// Configuration configuration = project.configurations.create(name) +// if (publication.dependencies != null) { +// if (publication.dependencies.implementation != null) { +// publication.dependencies.implementation.each { dependency -> +//// Logger.buildOutput("packJar: publication:${PublicationUtil.getPublicationId(publication)} move dependencies:${it}") +// if (dependency instanceof PublicationDependencyModuleOption) { +// project.dependencies.add(name, dependency.path) +// } else { +// project.dependencies.add(name, dependency) +// } +// } +// } +// if (publication.dependencies.compileOnly != null) { +// publication.dependencies.compileOnly.each { dependency -> +// if (dependency instanceof PublicationDependencyModuleOption) { +// project.dependencies.add(name, dependency.path) +// } else { +// project.dependencies.add(name, dependency) +// } +// } +// } +// } +// project.dependencies.add(name, PublicationUtil.getMavenGAV(publication)) +// try { +// configuration.copy().files.each { +// if (it.name.endsWith(fileName)) { +// filePath = it.absolutePath +// } +// } +// } catch (Exception e) { +//// e.printStackTrace() +// Logger.buildOutput(e.getMessage()) +// } +// project.configurations.remove(configuration) + return filePath != null + } + static boolean compareMavenJar(Project project, PublicationOption publication, String localPath) { String filePath = null - String fileName = publication.artifactId + "-" + publication.version + ".jar" + String fileName = publication.artifactId + "-" + publication.sdkVersion + ".jar" def name = "component[${publication.groupId}-${publication.artifactId}]Classpath" Configuration configuration = project.configurations.create(name) - project.dependencies.add(name, publication.groupId + ":" + publication.artifactId + ":" + publication.version) - configuration.copy().files.each { - if (it.name.endsWith(fileName)) { - filePath = it.absolutePath + project.dependencies.add(name, PublicationUtil.getMavenGAV(publication)) + try { + configuration.copy().files.each { + if (it.name.endsWith(fileName)) { + filePath = it.absolutePath + } } + } catch (Exception e) { +// e.printStackTrace() } project.configurations.remove(configuration) if (filePath == null) return false @@ -341,19 +446,26 @@ class JarUtil { return jarFile.absolutePath } - static void handleMavenJar(Project project, PublicationOption publication) { + static String handleMavenJar(Project project, PublicationOption publication) { + long startTime = System.currentTimeMillis() File target = new File(Runtimes.sSdkDir, PublicationUtil.getJarName(publication)) if (publication.invalid) { PublicationManager.getInstance().addPublication(publication) if (target.exists()) { target.delete() } - return + return "Handle Maven jar " + PublicationUtil.getJarName(publication) + " cost " + (System.currentTimeMillis() - startTime) + "ms" } + boolean isMavenJarExists = isMavenJarExists(project, publication) + boolean hasGitDiff = PublicationManager.getInstance().hasModifyWithGitDiff(publication.sdkSourceSet) + boolean hasModifiedSource = PublicationManager.getInstance().hasSdkModified(publication) - boolean hasModifiedSource = PublicationManager.getInstance().hasModified(publication) - - if (target.exists()) { + if (!hasGitDiff && isMavenJarExists) { + publication.invalid = false + publication.useLocal = false + PublicationManager.getInstance().addPublication(publication) + return "Handle Maven jar " + PublicationUtil.getJarName(publication) + " cost " + (System.currentTimeMillis() - startTime) + "ms" + } else if (target.exists()) { if (hasModifiedSource) { def releaseJar = JarUtil.packJavaSourceJar(project, publication, Runtimes.getAndroidJarPath(), Runtimes.getCompileOption(), true) if (releaseJar == null) { @@ -362,52 +474,30 @@ class JarUtil { if (target.exists()) { target.delete() } - return + return "Handle Maven jar " + PublicationUtil.getJarName(publication) + " cost " + (System.currentTimeMillis() - startTime) + "ms" } FileUtil.copyFile(releaseJar, target) } publication.invalid = false publication.useLocal = true PublicationManager.getInstance().addPublication(publication) - } else if (!hasModifiedSource) { - PublicationOption lastPublication = PublicationManager.getInstance().getPublication(publication.groupId, publication.artifactId) - if (lastPublication.version != publication.version) { - publication.versionNew = publication.version - publication.version = lastPublication.version - } - publication.invalid = false - publication.useLocal = false - PublicationManager.getInstance().addPublication(publication) - return + return "Handle Local jar " + PublicationUtil.getJarName(publication) + " cost " + (System.currentTimeMillis() - startTime) + "ms" } else { - def releaseJar = JarUtil.packJavaSourceJar(project, publication, Runtimes.getAndroidJarPath(), Runtimes.getCompileOption(), false) + def releaseJar = JarUtil.packJavaSourceJar(project, publication, Runtimes.getAndroidJarPath(), Runtimes.getCompileOption(), true) if (releaseJar == null) { publication.invalid = true PublicationManager.getInstance().addPublication(publication) if (target.exists()) { target.delete() } - return - } - - PublicationOption lastPublication = PublicationManager.getInstance().getPublication(publication.groupId, publication.artifactId) - if (lastPublication == null) { - lastPublication = publication - } - boolean equals = JarUtil.compareMavenJar(project, lastPublication, releaseJar.absolutePath) - if (equals) { - if (target.exists()) { - target.delete() - } - publication.useLocal = false - } else { - releaseJar = JarUtil.packJavaSourceJar(project, publication, Runtimes.getAndroidJarPath(), Runtimes.getCompileOption(), true) - FileUtil.copyFile(releaseJar, target) - publication.useLocal = true + return "Handle Maven jar " + PublicationUtil.getJarName(publication) + " cost " + (System.currentTimeMillis() - startTime) + "ms" } + FileUtil.copyFile(releaseJar, target) + publication.useLocal = true publication.invalid = false PublicationManager.getInstance().addPublication(publication) } + return "Handle Local jar " + PublicationUtil.getJarName(publication) + " cost " + (System.currentTimeMillis() - startTime) + "ms" } static String handleLocalJar(Project project, PublicationOption publication) { @@ -423,16 +513,17 @@ class JarUtil { } if (target.exists()) { - boolean hasModifiedSource = PublicationManager.getInstance().hasModified(publication) + boolean hasModifiedSource = PublicationManager.getInstance().hasSdkModified(publication) if (!hasModifiedSource) { publication.invalid = false publication.useLocal = true PublicationManager.getInstance().addPublication(publication) + Logger.buildOutput("${project.name} sdk nothing change,use local cache") return "Handle Local jar " + PublicationUtil.getJarName(publication) + " cost " + (System.currentTimeMillis() - startTime) + "ms" } } - File releaseJar = JarUtil.packJavaSourceJar(project, publication, Runtimes.getAndroidJarPath(), Runtimes.getCompileOption(), true) + File releaseJar = packJavaSourceJar(project, publication, Runtimes.getAndroidJarPath(), Runtimes.getCompileOption(), true) if (releaseJar == null) { publication.invalid = true PublicationManager.getInstance().addPublication(publication) diff --git a/component-plugin/src/main/groovy/com/plugin/component/utils/PublicationUtil.groovy b/component-plugin/src/main/groovy/com/plugin/component/utils/PublicationUtil.groovy index d27d666..15a2f00 100644 --- a/component-plugin/src/main/groovy/com/plugin/component/utils/PublicationUtil.groovy +++ b/component-plugin/src/main/groovy/com/plugin/component/utils/PublicationUtil.groovy @@ -1,6 +1,11 @@ package com.plugin.component.utils +import com.android.build.gradle.AppExtension +import com.plugin.component.ComponentPlugin import com.plugin.component.Constants +import com.plugin.component.extension.PublicationManager +import com.plugin.component.extension.option.sdk.PublicationDependencyModuleOption +import com.plugin.component.extension.option.sdk.SdkOption import com.plugin.component.log.Logger import com.plugin.component.Runtimes import com.plugin.component.extension.module.ProjectInfo @@ -8,8 +13,17 @@ import com.plugin.component.extension.module.SourceFile import com.plugin.component.extension.module.SourceSet import com.plugin.component.extension.option.sdk.PublicationOption import com.plugin.component.task.CompileSdkTask +import org.apache.http.util.TextUtils import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.ExcludeRule +import org.gradle.api.component.SoftwareComponent +import org.gradle.api.publish.PublishingExtension import org.gradle.api.publish.maven.MavenPublication +import org.gradle.api.tasks.TaskProvider + +import java.text.SimpleDateFormat class PublicationUtil { @@ -25,14 +39,23 @@ class PublicationUtil { return publication.groupId + '-' + publication.artifactId + '.jar' } + static getAarName(PublicationOption publication) { + return publication.groupId + '-' + publication.artifactId + '-imp' + '.aar' + } + static getMavenGAV(PublicationOption publication) { - return publication.groupId + ':' + publication.artifactId + ':' + publication.version + return publication.groupId + ':' + publication.artifactId + ':' + publication.sdkVersion + } + + static getImpMavenGAV(PublicationOption publication, boolean isRelease) { + return publication.groupId + ':' + publication.getArtifactIdString() + "-imp${isRelease ? '' : '-debug'}" + ':' + publication.implVersion + "@aar" } static getLocalGAV(PublicationOption publication) { return ':' + publication.groupId + '-' + publication.artifactId + ':' } + /** * 解析 component 依赖 * @param projectInfo @@ -57,12 +80,26 @@ class PublicationUtil { * @return */ static parseComponent(String value) { - value = value.replace(Constants.COMPONENT, "") + value = value.replace(Constants.COMPONENT_PRE, "") String key = ProjectUtil.getComponentValue(value) PublicationOption publication = Runtimes.getSdkPublication(key) return getPublication(publication) } + static parseComponentToPublicationId(String value) { + value = value.replace(Constants.COMPONENT_PRE, "") + String key = ProjectUtil.getComponentValue(value) + PublicationOption publication = Runtimes.getSdkPublication(key) + return getPublicationId(publication) + } + + static parseComponentDependency(String value) { + value = value.replace(Constants.COMPONENT_PRE, "") + String key = ProjectUtil.getComponentValue(value) + PublicationOption publication = Runtimes.getSdkPublication(key) + return publication + } + /** * 获取 publication 依赖 * @param publication @@ -92,7 +129,13 @@ class PublicationUtil { dependency = parseComponent(it) } Logger.buildOutput("compileOnly " + dependency) - compileOnly dependency + if (dependency instanceof String) { + compileOnly dependency + } else if (dependency instanceof PublicationDependencyModuleOption) { + compileOnly(dependency.path) { + exclude dependency.exclude + } + } } } if (publication.dependencies.implementation != null) { @@ -102,7 +145,13 @@ class PublicationUtil { dependency = parseComponent(it) } Logger.buildOutput("implementation " + dependency) - implementation dependency + if (dependency instanceof String) { + implementation dependency + } else if (dependency instanceof PublicationDependencyModuleOption) { + implementation(dependency.path) { + exclude dependency.exclude + } + } } } } @@ -113,91 +162,273 @@ class PublicationUtil { * @param project * @param publication */ - static void initPublication(Project project, PublicationOption publication) { + static void initPublication(Project project, PublicationOption publication, boolean isAutoVersion) { String displayName = project.getDisplayName() publication.project = displayName.substring(displayName.indexOf("'") + 1, displayName.lastIndexOf("'")) - def buildSdk = new File(project.projectDir, publication.isSdk ? Constants.BUILD_SDK_DIR : Constants.BUILD_IMPL_DIR) - publication.sourceSetName = publication.scrName + initSdkSourceSet(project, publication, isAutoVersion) + initImpSourceSet(project, publication, isAutoVersion) + + } + + /** + * 收集实现类相关的信息 + * @param project + * @param publication + */ + private static void initImpSourceSet(Project project, PublicationOption publication, boolean isAutoVersion) { + def buildSdk = new File(project.projectDir, Constants.BUILD_IMPL_DIR) + publication.impDir = new File(buildSdk, publication.scrName) + + SourceSet impSourceSet = new SourceSet() + def impDir = project.projectDir + impSourceSet.path = impDir.absolutePath + impSourceSet.lastModifiedSourceFile = new HashMap<>() + if (isAutoVersion && !publication.useUserImplVersion) { + String split = "@_@" + String gitInfo = FileUtil.shell("git log --max-count=1 --pretty=format:\"%cd${split}%h${split}%cn${split}%s\" --date=format:'%Y%m%d%H%M%S' ${impDir.absolutePath}") + Logger.buildOutput("filename:$impDir.absolutePath gitInfo:$gitInfo") + String[] infos = gitInfo.split(split) + if (infos.size() == 4) { + String commitTime = infos[0] + String gitHash = infos[1] + String commitUser = infos[2] + String commitInfo = infos[3] + String version = "${commitTime}_${gitHash}" + Logger.buildOutput("filename:$impDir.absolutePath git最近提交:$version") + if (!TextUtils.isEmpty(version)) { + impSourceSet.gitVersion = version + publication.implVersion = version + impSourceSet.commitTime = commitTime + impSourceSet.commitUser = commitUser + impSourceSet.gitCommitInfo = commitInfo + } + } + } + def sourceDir + if (publication.sourceSetName.contains('/')) { + sourceDir = new File(project.projectDir, publication.sourceSetName) + } else { + sourceDir = new File(project.projectDir, 'src/' + publication.sourceSetName) + } + long quickVerify = 0L + project.fileTree(sourceDir).each { + SourceFile sourceFile = new SourceFile() + sourceFile.path = it.path + sourceFile.lastModified = it.lastModified() + impSourceSet.lastModifiedSourceFile.put(sourceFile.path, sourceFile) + quickVerify += it.lastModified() + } + //检测build.gradle和proguard-rules.pro文件是否有变化 + File buildGradle = new File(project.projectDir, 'build.gradle') + if (buildGradle.exists()) { + SourceFile sourceFile = new SourceFile() + sourceFile.path = buildGradle.path + sourceFile.lastModified = buildGradle.lastModified() + impSourceSet.lastModifiedSourceFile.put(sourceFile.path, sourceFile) + quickVerify += buildGradle.lastModified() + } + File proguardRules = new File(project.projectDir, 'proguard-rules.pro') + if (proguardRules.exists()) { + SourceFile sourceFile = new SourceFile() + sourceFile.path = proguardRules.path + sourceFile.lastModified = proguardRules.lastModified() + impSourceSet.lastModifiedSourceFile.put(sourceFile.path, sourceFile) + quickVerify += proguardRules.lastModified() + } + + impSourceSet.quickVerifyModified = quickVerify + publication.impSourceSet = impSourceSet + publication.impNeedPack = !impSourceSet.lastModifiedSourceFile.isEmpty() + } + + private static void initSdkSourceSet(Project project, PublicationOption publication, boolean isAutoVersion) { + def buildSdk = new File(project.projectDir, Constants.BUILD_SDK_DIR) publication.buildDir = new File(buildSdk, publication.scrName) - SourceSet misSourceSet = new SourceSet() - def misDir + SourceSet sdkSourceSet = new SourceSet() + def sdkDir if (publication.sourceSetName.contains('/')) { - misDir = new File(project.projectDir, publication.sourceSetName + '/sdk/') + sdkDir = new File(project.projectDir, publication.sourceSetName + '/sdk/') } else { - misDir = new File(project.projectDir, 'src/' + publication.sourceSetName + '/sdk/') + sdkDir = new File(project.projectDir, 'src/' + publication.sourceSetName + '/sdk/') } - misSourceSet.path = misDir.absolutePath - misSourceSet.lastModifiedSourceFile = new HashMap<>() - project.fileTree(misDir).each { + sdkSourceSet.path = sdkDir.absolutePath + sdkSourceSet.lastModifiedSourceFile = new HashMap<>() + long quickVerify = 0L + if (isAutoVersion && !publication.useUserSdkVersion) { + String split = "@_@" + String gitInfo = FileUtil.shell("git log --max-count=1 --pretty=format:\"%cd${split}%h${split}%cn${split}%s\" --date=format:'%Y%m%d%H%M%S' ${sdkDir.absolutePath}") + Logger.buildOutput("filename:$sdkDir.absolutePath gitInfo:$gitInfo") + String[] infos = gitInfo.split(split) + if (infos.size() == 4) { + String commitTime = infos[0] + String gitHash = infos[1] + String commitUser = infos[2] + String commitInfo = infos[3] + String version = "${commitTime}_${gitHash}" + Logger.buildOutput("filename:$sdkDir.absolutePath git最近提交:$version") + if (!TextUtils.isEmpty(version)) { + sdkSourceSet.gitVersion = version + publication.sdkVersion = version + sdkSourceSet.commitTime = commitTime + sdkSourceSet.commitUser = commitUser + sdkSourceSet.gitCommitInfo = commitInfo + } + } + } + project.fileTree(sdkDir).each { if (FileUtil.isValidPackSource(it)) { SourceFile sourceFile = new SourceFile() sourceFile.path = it.path sourceFile.lastModified = it.lastModified() - misSourceSet.lastModifiedSourceFile.put(sourceFile.path, sourceFile) + quickVerify += it.lastModified() + sdkSourceSet.lastModifiedSourceFile.put(sourceFile.path, sourceFile) } } + sdkSourceSet.quickVerifyModified = quickVerify - publication.misSourceSet = misSourceSet - publication.invalid = misSourceSet.lastModifiedSourceFile.isEmpty() + publication.sdkSourceSet = sdkSourceSet + publication.invalid = quickVerify == 0L } static void createPublishingPublication(Project project, PublicationOption publication, String publicationName) { - def publishing = project.extensions.getByName('publishing') + PublishingExtension publishing = project.extensions.getByName(Constants.PUBLISHING) MavenPublication mavenPublication = publishing.publications.maybeCreate(publicationName, MavenPublication) mavenPublication.groupId = publication.groupId mavenPublication.artifactId = publication.artifactId - mavenPublication.version = publication.versionNew != null ? publication.versionNew : publication.version + mavenPublication.version = publication.versionNew != null ? publication.versionNew : publication.sdkVersion mavenPublication.pom.packaging = 'jar' + publishing.repositories(Runtimes.sSdkOption.repositories) + File target = new File(Runtimes.sSdkDir, PublicationUtil.getJarName(publication)) def outputsDir = new File(publication.buildDir, "outputs") - mavenPublication.artifact source: new File(outputsDir, "classes.jar") + mavenPublication.artifact source: target mavenPublication.artifact source: new File(outputsDir, "classes-source.jar"), classifier: 'sources' - - if (publication.dependencies != null) { - mavenPublication.pom.withXml { - def dependenciesNode = asNode().appendNode('dependencies') - if (publication.dependencies.implementation != null) { - publication.dependencies.implementation.each { - def gav = it.split(":") - if (gav[1].startsWith(Constants.SDK_PRE)) { - PublicationOption dependencyPublication = publicationManager.getPublicationByKey(gav[1].replace(Constants.SDK_PRE, '')) - if (dependencyPublication.useLocal) { - throw new RuntimeException("component publication [$dependencyPublication.groupId:$dependencyPublication.artifactId] has not publish yet.") - } - } - def dependencyNode = dependenciesNode.appendNode('dependency') - dependencyNode.appendNode('groupId', gav[0]) - dependencyNode.appendNode('artifactId', gav[1]) - dependencyNode.appendNode('version', gav[2]) - dependencyNode.appendNode('scope', 'implementation') - } - } - } - } + //todo 存在找不到引用问题 暂不处理 +// if (publication.dependencies != null) { +// mavenPublication.pom.withXml { +// def dependenciesNode = asNode().appendNode('dependencies') +// if (publication.dependencies.implementation != null) { +// publication.dependencies.implementation.each { +// String[] gav +// def exclusion +// if (it instanceof PublicationDependencyModuleOption) { +// gav = it.path.split(":") +// exclusion = it.exclude +// } else { +// gav = it.split(":") +// } +// if (gav[1].startsWith(Constants.SDK_PRE)) { +// PublicationOption dependencyPublication = publicationManager.getPublicationByKey(gav[1].replace(Constants.SDK_PRE, '')) +// if (dependencyPublication.useLocal) { +// throw new RuntimeException("component publication [$dependencyPublication.groupId:$dependencyPublication.artifactId] has not publish yet.") +// } +// } +// if (gav.size() == 3) { +// //todo 存在本地引用的情况 +// def dependencyNode = dependenciesNode.appendNode('dependency') +// dependencyNode.appendNode('groupId', gav[0]) +// dependencyNode.appendNode('artifactId', gav[1]) +// dependencyNode.appendNode('version', gav[2]) +// dependencyNode.appendNode('scope', 'implementation') +// if (exclusion != null) { +// def exclusionsNode = dependencyNode.appendNode('exclusions') +// def exclusionNode = exclusionsNode.appendNode('exclusion') +// exclusionNode.appendNode('groupId', exclusion['group']) +// exclusionNode.appendNode('artifactId', exclusion['module']) +// } +// } +// } +// } +// } +// } } - static void createPublishTask(Project project, PublicationOption publication) { - def taskName = 'compileMis[' + publication.artifactId + ']Source' + static Task createPublishTask(Project project, Project androidProject, PublicationOption publication) { + +// for test +// TaskProvider cleanTaskProvider = project.tasks.named("clean") +// Task cleanTask = cleanTaskProvider.get() + + def taskName = 'compile[' + publication.artifactId + ']Source' def compileTask = project.getTasks().findByName(taskName) if (compileTask == null) { compileTask = project.getTasks().create(taskName, CompileSdkTask.class) compileTask.publication = publication - compileTask.dependsOn 'clean' } +// cleanTask.finalizedBy(compileTask) - def publicationName = 'component[' + publication.artifactId + ']' + def publicationName = 'Component-' + project.name + 'Sdk' String publishTaskNamePrefix = "publish${publicationName}PublicationTo" project.tasks.whenTaskAdded { if (it.name.startsWith(publishTaskNamePrefix)) { it.dependsOn compileTask it.doLast { - new File(misDir, PublicationUtil.getJarName(publication)).delete() + publication.invalid = false + publication.useLocal = false + PublicationManager.getInstance().addPublication(publication) } } } - PublicationUtil.createPublishingPublication(project, publication, publicationName) + createPublishingPublication(project, publication, publicationName) + TaskProvider publishTask = project.tasks.named("${publishTaskNamePrefix}MavenRepository") + compileTask.finalizedBy(publishTask.get()) + + return compileTask + } + + static void createImpPublishingPublication(Project project, PublicationOption publication, String publicationName, boolean isRelease) { + def publishing = project.extensions.getByName(Constants.PUBLISHING) + MavenPublication mavenPublication = publishing.publications.maybeCreate(publicationName, MavenPublication) + File target = new File(Runtimes.sImplDir, getAarName(publication)) + if (mavenPublication.getArtifacts().size() != 0) { + //fixme 这里会执行两次所以过滤一下 + return + } + mavenPublication.groupId = publication.groupId + if (isRelease) { + mavenPublication.artifactId = publication.getArtifactIdString() + "-imp" + } else { + mavenPublication.artifactId = publication.getArtifactIdString() + "-imp-debug" + } + mavenPublication.version = publication.versionNew != null ? publication.versionNew : publication.implVersion + mavenPublication.pom.packaging = 'aar' + Logger.buildOutput("createImpPublishingPublication ${publicationName} Artifacts:${mavenPublication.getArtifacts().size()}") + mavenPublication.getArtifacts().clear() + mavenPublication.artifact(target.absolutePath) + publishing.repositories(Runtimes.sSdkOption.repositories) +// mavenPublication.pom.withXml { +// def dependenciesNode = asNode().appendNode('dependencies') +// def addDependency = { Dependency dep, String scope -> +// if (dep.group == null || dep.version == null || dep.name == null || dep.name == "unspecified") +// return // ignore invalid dependencies +// +// final dependencyNode = dependenciesNode.appendNode('dependency') +// dependencyNode.appendNode('groupId', dep.group) +// dependencyNode.appendNode('artifactId', dep.name) +// dependencyNode.appendNode('version', dep.version) +// dependencyNode.appendNode('scope', scope) +// +// if (!dep.transitive) { +// // If this dependency is transitive, we should force exclude all its dependencies them from the POM +// final exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion') +// exclusionNode.appendNode('groupId', '*') +// exclusionNode.appendNode('artifactId', '*') +// } else if (!dep.properties.excludeRules.empty) { +// // Otherwise add specified exclude rules +// final exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion') +// dep.properties.excludeRules.each { ExcludeRule rule -> +// exclusionNode.appendNode('groupId', rule.group ?: '*') +// exclusionNode.appendNode('artifactId', rule.module ?: '*') +// } +// } +// } +// +// project.configurations.compile.getDependencies().each { dep -> addDependency(dep, "compile") } +// project.configurations.api.getDependencies().each { dep -> addDependency(dep, "api") } +// project.configurations.implementation.getDependencies().each { dep -> addDependency(dep, "implementation") } +// } } /** @@ -210,7 +441,8 @@ class PublicationUtil { List compileOnly = new ArrayList<>() publication.dependencies.compileOnly.each { if (it instanceof String && it.startsWith(Constants.COMPONENT_PRE)) { - PublicationOption existPublication = PublicationManager.getInstance().getPublicationByKey(getPublicationId(publication)) +// PublicationOption existPublication = PublicationManager.getInstance().getPublicationByKey(getPublicationId(publication)) + PublicationOption existPublication = parseComponentDependency(it) if (existPublication != null) { if (existPublication.useLocal) { compileOnly.add(getLocalGAV(existPublication)) @@ -227,13 +459,17 @@ class PublicationUtil { if (publication.dependencies.implementation != null) { List implementation = new ArrayList<>() publication.dependencies.implementation.each { +// Logger.buildOutput("filterDependencies : publication:${getPublicationId(publication)} implementation: ${it}") if (it instanceof String && it.startsWith(Constants.COMPONENT_PRE)) { - PublicationOption existPublication = PublicationManager.getInstance().getPublicationByKey(getPublicationId(publication)) +// PublicationOption existPublication = PublicationManager.getInstance().getPublicationByKey(getPublicationId(publication)) + PublicationOption existPublication = parseComponentDependency(it) if (existPublication != null) { if (existPublication.useLocal) { implementation.add(getLocalGAV(existPublication)) +// Logger.buildOutput("filterDependencies : publication:${getPublicationId(publication)} implementation useLocalGav: ${getLocalGAV(existPublication)}") } else { implementation.add(getMavenGAV(existPublication)) +// Logger.buildOutput("filterDependencies : publication:${getPublicationId(publication)} implementation useMavenGav: ${getMavenGAV(existPublication)}") } } } else { @@ -244,4 +480,44 @@ class PublicationUtil { } } } + + static void handleSdkPublish(Project childProject, Project androidProject, PublicationOption publication, Task dependTask) { + if (publication.sdkVersion != null && publication.sdkNeedPublish) { + if (JarUtil.isMavenJarExists(childProject, publication)) { + publication.sdkNeedPublish = false + return + } + childProject.plugins.apply(Constants.PLUGIN_MAVEN_PUBLISH) + Logger.buildOutput("need publish sdk:${publication.name}") + Task compileTask = createPublishTask(childProject, androidProject, publication) + if (dependTask == null) { + AppExtension appExtension = androidProject.extensions.getByType(AppExtension.class) + appExtension.applicationVariants.each { + TaskProvider assembleTask = AarUtil.getAssembleTask(androidProject, it) + Logger.buildOutput("getAssembleTask:${assembleTask.name}") + dependTask = assembleTask.get() + dependTask.finalizedBy(compileTask) + } + } else { + compileTask.dependsOn(dependTask) + dependTask.finalizedBy(compileTask) + } + } + } + + static void handleImplPublish(Project project, PublicationOption publication, boolean isDebug, Task dependTask) { + Logger.buildOutput("Handle ${publication.name} ImplPublish,isDebug:${isDebug}") + boolean isAarExists = AarUtil.isArrExits(project, publication, !isDebug) + if (isAarExists) { + publication.impNeedPublish = false + return + } + project.plugins.apply(Constants.PLUGIN_MAVEN_PUBLISH) + if (isDebug) { + //注册打包arr task + AarUtil.packImpAar(project, ComponentPlugin.androidProject, publication, dependTask) + } else { + AarUtil.packImpAarRelease(project, ComponentPlugin.androidProject, publication, dependTask) + } + } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 72d310a..6a5de5a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip diff --git a/gradleScript/component.gradle b/gradleScript/component.gradle index 4d82c62..610f08c 100644 --- a/gradleScript/component.gradle +++ b/gradleScript/component.gradle @@ -23,6 +23,25 @@ component { targetCompatibility JavaVersion.VERSION_1_8 } + //组件发布相关 + //是否自动化版本,依赖git +// autoVersion true + //是否发布模式 +// publishMode false + //maven寻址地址与下面地址一直 +// mavenUrl "$ex_private_maven_repository" + //maven发布仓库配置 +// repositories { +// maven { +// allowInsecureProtocol true +// credentials { +// username = "$ex_private_maven_username" +// password = "$ex_private_maven_password" +// } +// url = "$ex_private_maven_repository" +// } +// } + configuration { 'library' { @@ -41,62 +60,64 @@ component { implementation 'com.google.code.gson:gson:2.8.1' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" + implementation component(":library")//SDK依赖其他组件 } } } } - pin { - configuration { - 'pins' { - codeCheckEnabled true - //参与编译的模块 - include ':p_base',':p_common',":p_home" - //对外把暴露的模块 - export ':main' - } - } - } + //1.0.10版本暂不支持 +// pin { +// configuration { +// 'pins' { +// codeCheckEnabled true +// //参与编译的模块 +// include ':p_base',':p_common',":p_home" +// //对外把暴露的模块 +// export ':main' +// } +// } +// } - //组件调试声明 - debug { - - //调试模块,随意在当前项目新建一个module用于调试即可 - targetModuleName 'debugModule' - - //申明调试模块运行时链接组件的资源,表明运行 debugModule 时使用目录资源 - targetDebugName 'library' - - configuration { - - 'library' { - dependencies { - //只支持 implementation,足够了 - implementation component(':library') - } - } - - 'libraryKotlin' { - dependencies { - implementation component(':libraryKotlin') - } - } - - 'libraryWithoutPlugin' { - dependencies { - implementation project(':libraryWithoutPlugin') - } - } - - 'customDebug' { - dependencies { - implementation component(':library') - implementation component(':libraryKotlin') - } - } - - } - } + //组件调试声明1.0.10版本暂不支持 +// debug { +// +// //调试模块,随意在当前项目新建一个module用于调试即可 +// targetModuleName 'debugModule' +// +// //申明调试模块运行时链接组件的资源,表明运行 debugModule 时使用目录资源 +// targetDebugName 'library' +// +// configuration { +// +// 'library' { +// dependencies { +// //只支持 implementation,足够了 +// implementation component(':library') +// } +// } +// +// 'libraryKotlin' { +// dependencies { +// implementation component(':libraryKotlin') +// } +// } +// +// 'libraryWithoutPlugin' { +// dependencies { +// implementation project(':libraryWithoutPlugin') +// } +// } +// +// 'customDebug' { +// dependencies { +// implementation component(':library') +// implementation component(':libraryKotlin') +// } +// } +// +// } +// } } diff --git a/settings.gradle b/settings.gradle index e760fbd..ce8ae0d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,8 +5,8 @@ //include ':component-plugin' //调试插件 -//includeBuild './component-plugin' +includeBuild './component-plugin' -include ':app', ':pins' -include ':debugModule' +include ':app'/*, ':pins'*/ +//include ':debugModule' include ':library', ':libraryKotlin', ':libraryWithoutPlugin'