使用Gradle插件上传组件到Maven之源码上传

为什么要上传源码?

我们通过自定义插件的方式完成了上传一个组件到Maven仓库的功能,不过上传的aar或jar中并没有包含源码,如果依赖没有源码的组件出问题时不容易进行调试,所以我希望能够通过一个开关动态的打开/关闭源码的上传功能,如果对内发布就开放源码上传,如果对外发布就关闭。

在脚本中如何实现?

如果使用脚本开发的上传功能的话,可以通过如下的方式进行源码配置:

在Android模块

//上传脚本中新增task,类型为Jar
task androidSourceJar(type: Jar) {
//    from project.android.sourceSets.main.java.sourceFiles //不包括Kotlin文件
    from project.android.sourceSets.main.java.getSrcDirs() //包括Kotlin文件
    archiveClassifier = 'sources'
}

afterEvaluate {
    publishing {
        publications {
            //配置生成上传release节点的task
            release(MavenPublication) {
                from components.release
                artifact androidSourceJar //设置artifact为上面定义的task

                groupId = libGroup
                artifactId = libArtifactId
                version = libVersion
            }
        }
      //....省略其他部分代码
    }
} 

通过上面的修改,无源码的1.0.1版本和有源码的1.0.2版本对比如下:

img_01.png
这样的话该组件的使用者在组件出现问题时,就可以进行静态代码分析或动态代码调试去解决问题了。

在Java模块

在Java模块中,与Android基本相似,只有细微的差别,如下:

// 上传脚本中新增task,类型为Jar 
task sourceJar(type: Jar) {
    from sourceSets.main.allSource
    archiveClassifier = 'sources'
}

publishing {
    publications {
        //配置生成上传Java节点的task
        mavenJava(MavenPublication) {
            from components.java
            artifact sourceJar //设置artifact为上面定义的task

            groupId = libGroup
            artifactId = libArtifactId
            version = libVersion
        }
    }
		// ....省略其他部分代码
} 

通过上面的配置,就可以起到Java库的源码上传功能。

在插件中如何实现?

其实在插件中配置源码上传的功能与在脚本中配置完全一致,但区别的就是API上又些许不同。

首先为了能够动态的打开/关闭源码上传的功能,我们在_Publisher_参数中新增一个名为_withSource_的Boolean属性。

open class Publisher {
  //...省略部分代码
  
  //是否携带源码发布到maven仓库
  var withSource: Boolean = false  //此为新增
} 

然后在自定义Task中做如下修改:

open class PublisherTask : DefaultTask() {

    //是否完成执行任务
    private var executeFinishFlag: AtomicBoolean = AtomicBoolean(false)

    //检验状态是否通过
    private var checkStatus = false

  	//自定义参数
    private lateinit var publisher: Publisher

    //生成的上传Maven仓库的task名称
    private var currentPublishTaskName: String? = null

    init {
        group = "upload"

        project.run {
            publisher = extensions.getByName(Publisher.NAME) as Publisher

            //动态为该模块引入上传插件
            apply(hashMapOf<String, String>(Pair("plugin", "maven-publish")))
            val publishing = project.extensions.getByType(PublishingExtension::class.java)

            //当前模块是否为android模块,当前只支持android和java模块 非android即java
            val isAndroidModule = project.plugins.hasPlugin("com.android.library")
            val dynamicGenerateSourceTask = dynamicGenerateSourceTask(isAndroidModule)

            afterEvaluate {
                components.forEach {
                    if (isAndroidModule) {
                        //android只注册release的发布,如果需要all/debug或更多其他的可以自定义注册
                        if (it.name == "release") {
                            doRegisterTask(publishing, it.name, it, dynamicGenerateSourceTask)
                        }
                    } else {
                        //Java只注册java的发布,如果需要kotlin或更多其他的可以自定义注册
                        if (it.name == "java") {
                            doRegisterTask(publishing, it.name, it, dynamicGenerateSourceTask)
                        }
                    }
                }
            }
        }
    }

		//注册上传maven仓库的名称
    private fun doRegisterTask(
        publishing: PublishingExtension,
        name: String,
        component: SoftwareComponent,
        withSourceTask: Task?,
    ) {

        currentPublishTaskName = "publish${name}PublicationToMavenRepository"

        publishing.publications { publications ->
            if (publications.findByName(name) == null) {
                publications.create("$name",
                    MavenPublication::class.java) { publication ->
                    publication.groupId = publisher.libGroup
                    publication.artifactId = publisher.libArtifact
                    publication.version = publisher.libVersion

                    //如果支持携带源码,则配置 artifact 为源码task 
                    withSourceTask?.let {
                        publication.artifact(it)
                    }

                    publication.from(component)
                }

                publishing.repositories { artifactRepositories ->

                    artifactRepositories.maven { mavenArtifactRepository ->

                        mavenArtifactRepository.url =
                            if (publisher.libVersion.endsWith("SNAPSHOT")) {
                                URI(publisher.repoSnapshot)
                            } else {
                                URI(publisher.repoRelease)
                            }

                        mavenArtifactRepository.credentials { credentials ->
                            credentials.username = publisher.repoAccount
                            credentials.password = publisher.repoPassword
                        }
                    }
                }
            }
        }
    }

    /**
     * 新增 动态生成源码task
     * @param isAndroidModule 是否为android模块
     */
    private fun dynamicGenerateSourceTask(isAndroidModule: Boolean): Task? {

        if (!publisher.withSource) {
            return null
        }

        // 其实taskName叫什么都无所谓,但是处于规范,还是这么命名吧
        val taskName = if (isAndroidModule) {
            "androidSourcesJar"
        } else {
            "sourcesJar"
        }
        //这个地方的api跟脚本中所有不同
        val sourceSetFiles = if (isAndroidModule) {
            //获取build.gradle中的android节点
            val androidSet = project.extensions.getByName("android") as LibraryExtension
            val sourceSet = androidSet.sourceSets
            //获取android节点下的源码目录
            sourceSet.findByName("main")?.java?.srcDirs
        } else {
            //获取java模块中的源码目录
            val plugin = project.convention.getPlugin(JavaPluginConvention::class.java)
            plugin.sourceSets.findByName("main")?.allSource
        }

        //查找或注册源码task
        return project.tasks.findByName(taskName) ?: project.tasks.create(taskName,
            Jar::class.java) {
            it.from(sourceSetFiles)
            it.archiveClassifier.set("sources")
        }
    }

    @TaskAction
    fun doTask() {

        executeTask()

        //开启线程守护,防止子线程任务还没执行完毕,task就已经结束了
        while (!executeFinishFlag.get()) {
            Thread.sleep(500)
        }
    }

    private fun isWindows(): Boolean {
        return System.getProperty("os.name").toLowerCase().contains("windows")
    }

    private fun executeTask() {
        //1、对publisher配置的信息进行基础校验
        //2、把publisher上传到服务器端,做版本重复性校验
        checkStatus = requestCheckVersion()

        //如果前两步都校验通过了,checkStatus设置为true

        //解决可能发生的模块嵌套问题
        val realTaskName =
            project.projectDir.absolutePath
                .removePrefix(project.rootDir.absolutePath)
                .replace(File.separator, ":") + ":$currentPublishTaskName"

        //适配windows环境
        val exeCommand = if (isWindows()) {
            "${project.rootDir}${File.separator}gradlew.bat"
        } else {
            "${project.rootDir}${File.separator}gradlew"
        }


        if (checkStatus) {
            val out = ByteArrayOutputStream()
            //通过命令行的方式进行调用上传maven的task
            project.exec { exec ->
                exec.standardOutput = out
                exec.isIgnoreExitValue = true
                exec.commandLine(
                    exeCommand,
                    realTaskName
                )
            }
            val result = out.toString()
            if (result.contains("BUILD SUCCESSFUL")) {
                //上传maven仓库成功,上报到服务器
                val isSuccess = requestUploadVersion()
                if (isSuccess) {
                    //提示成功信息
                } else {
                    //提示错误信息
                }
                executeFinish()
            } else {
                throw Exception("上传Maven仓库失败,请检查配置!")
            }
        }
    }

    private fun requestCheckVersion(): Boolean {
        //TODO 上报服务器进行版本检查,这里直接模拟返回成功
        return true
    }

    private fun requestUploadVersion(): Boolean {
        //TODO 上报服务器进行版本更新操作,这里直接模拟返回成功
        return true
    }

    /**
     * 任务执行完毕
     */
    private fun executeFinish() {
        executeFinishFlag.set(true)
    }
} 

之后我们就可以在build.gradle中publisher节点下修改_withSource_的值来动态的开关源码支持了。

 publisher {
    //...省略其他配置
    //发布人
    publisher = "wangyumdao"
    withSource = true //开启源码上传脑功能
} 

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接扫码免费领取↓↓↓

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值