模块化后的 Android App 自动构建(一)

2,304 阅读4分钟
211人阅读

很多关于Jenkins的Android的构建,基本都是用gradle构建整个项目的。但是模块化后,不同模块就存在不同项目里,即不同的git地址。而且,上传代码的时候,只会上传模块的源代码,不会上传根目录下的配置文件,如:  gradle.propertiesbuild.gradlelocal.propertiessettings.gradle等等。

这里就讲讲,当拆成一个个module后,我们是如何使用Jenkins进行自动构建的。分为一、二两篇,分别讲library的构建和app的构建。

前提条件:

1、在主机上安装上JenkinsjdkgradleAndroid sdk; 
2、在主机上搭建自己的maven私服

首先本文先说说子模块library的构建。


一、子模块library的构建

1、首先给出子模块的build.gradle示例:


apply plugin: 'com.android.library'
 
version = '1.1.5'
description = 'shop chat library'
ext.pom_packaging = 'aar'
ext.pom_groupId = 'com.showjoy.shop'
ext.pom_artifactId = 'chat'
ext.pom_name = 'shopandroid_chat'
ext.pom_snapshot = false
 
android {
    useLibrary 'org.apache.http.legacy'
}
 
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile('com.showjoy.shop:common:1.7.7')
 
    compile('com.showjoy.shop:webview:1.2.1')
 
    compile 'com.android.support:appcompat-v7:24.0.0'
}
 
apply from: System.properties['libraryBaseGradle']

以上的配置,打包的结果会生成 shopandroid_chat-release-1.1.5.aar,然后自动上传到maven仓库。这样简单的配置,把共同的gradle代码都封装到了 libraryBaseGradle里,每个模块的 build.gradle都依赖它,然后只要配置自己的 dependencies和模块的 名称和版本信息即可。

构建完成后,在其他模块里依赖即可使用:

compile 'com.showjoy.shop:chat:1.1.5'

当然需要在project 根目录的build.gradle配置上自己的maven 私服地址:


allprojects {
    repositories {
        maven {url 'http://192.168.0.62:8081/repository/maven-releases/'}
        maven {url 'http://192.168.0.62:8081/repository/maven-snapshots/'}
        jcenter()
        mavenCentral()
    }
}

2、libraryBaseGradle.gradle

封装的共同配置还是要看 System.properties['libraryBaseGradle'] 这个参数配置的gradle文件,这个参数在project 根目录下的gradle.properties : 
示例如下:

systemProp.compileSdkVersion=23
android.useDeprecatedNdk=true
org.gradle.daemon=true
org.gradle.parallel=true
systemProp.buildToolsVersion=23.0.2
systemProp.targetSdkVersion=21
org.gradle.jvmargs=-XX\:MaxPermSize\=4096m
org.gradle.configureondemand=true
systemProp.minSdkVersion=14
systemProp.scheme=showjoyshop
systemProp.libraryBaseGradle=http://git.xxxxxx.com/android/mvn-repo/raw/master/library.gradle

http://git.xxxxxx.com/android/mvn-repo/raw/master/library.gradle 的内容如下:


apply plugin: 'com.android.library'
apply plugin: 'maven'
 
android {
    compileSdkVersion Integer.parseInt(System.properties['compileSdkVersion'])
    buildToolsVersion System.properties['buildToolsVersion']
 
    defaultConfig {
        minSdkVersion Integer.parseInt(System.properties['minSdkVersion'])
        targetSdkVersion Integer.parseInt(System.properties['targetSdkVersion'])
    }
 
    lintOptions {
        abortOnError false
    }
 
 
    libraryVariants.all {
        variant ->
            variant.outputs.each {
                output ->
                    def outputFile = output.outputFile
                    if (outputFile != null && outputFile.name.endsWith('.aar')) {
                        def fileName = outputFile.name.replace(".aar", "-" + version + ".aar")
                        output.outputFile = new File(outputFile.parent, fileName)
                    }
            }
    }
 
    uploadArchives {
        repositories {
            mavenDeployer {
                if (pom_snapshot) {
                    repository (url: 'http://192.168.0.62:8081/repository/maven-snapshots/') {
                        authentication(userName: 'admin', password: 'xxxxxx')
                    }
                    version = version + '-SNAPSHOT'
                } else {
                    repository (url: 'http://192.168.0.62:8081/repository/maven-releases/') {
                        authentication(userName: 'admin', password: 'xxxxxx')
                    }
                }
                pom.version = version
                pom.artifactId = pom_artifactId
                pom.groupId = pom_groupId
                pom.name = pom_name
                pom.packaging = pom_packaging
 
                pom.project {
                    description description
                    dependencies {
                        project.configurations.collectMany { it.allDependencies }
                                .findAll { it instanceof ProjectDependency ||
                                it instanceof ExternalModuleDependency}
                        .each { dep ->
                            dependency {
                                groupId dep.getGroup()
                                artifactId dep.getName()
                                version dep.getVersion()
                                scope 'compile'
                            }
                        }
                    }
                }
            }
        }
    }
}

3、Jenkins的配置

在Jenkins上正常new item,基本信息配置没有什么特别,按照自己需要填写即可,主要讲三个注意点:

  • git地址的配置

-1484068595630.png

虽然后面构建的时候没有用到这里的配置,但是这里的配置作用在于每次构建能检查到changes

-1484068929404.png

  • build部分,如图:

    -1484068522067.png

    这里,使用了Python脚本进行构建

python ${JENKINS_HOME}/workspace/publish/jenkins/library/build.py ${JOB_NAME} "git@git.showjoy.net:shopandroid/shopandroid_chat.git"

其中,${JENKINS_HOME}/workspace/publish/jenkins/library/build.py 是构建脚本放置在主机上的路径,${JENKINS_HOME} 表示Jenkins的安装目录。后面跟了两个参数,参数一:job 名称${JOB_NAME} ,参数二:项目git地址

  • 构建结果处理

-1484068861964.png

这里配置的目的在于每次构建完成后,能知道是否构建成功了,以及构建的版本,如:

-1484068911160.png

4、构建脚本build.py

  • 整体的思路就是新建一个目录sourcecode,然后将project根目录下需要的文件(setting.gradle, build.gradle,gradle.propertiesgradlew,local.properties)从指定目录复制到sourcecode。其中注意 local.properties配置的sdk路径需要是主机上sdk的安装目录。

  • 然后动态修改setting.gradle只包含当前library的module name,然后clone library的代码到sourcecode,然后用 gradle clean build uploadArchives构建。

代码如下:

#coding:utf-8
OUTPUTS_PATH = "/var/lib/jenkins/workspace/"
 
GRADL_HOME = "/root/gradle-2.10/bin/"
 
SOURCE_CODE = "sourcecode"
 
MODULE_NAME = "shopandroid"
 
 
 
BRANCH  = "master"
 
def copyFiles(sourceDir,  targetDir): #把某一目录下的所有文件复制到指定目录中 
     for file in os.listdir(sourceDir): 
         sourceFile = os.path.join(sourceDir,  file) 
         targetFile = os.path.join(targetDir,  file) 
         if os.path.isfile(sourceFile): 
             if not os.path.exists(targetDir): 
                 os.makedirs(targetDir) 
             if not os.path.exists(targetFile) or(os.path.exists(targetFile) and (os.path.getsize(targetFile) != os.path.getsize(sourceFile))): 
                     open(targetFile, "wb").write(open(sourceFile, "rb").read()) 
         if os.path.isdir(sourceFile): 
             First_Directory = False 
             copyFiles(sourceFile, targetFile) 
 
def chdir(dir):
    os.chdir(dir)
    print ('当前目录:' + os.getcwd())
 
 
def getVersionName(path):
    import re
    versionName = ""
    f = open(path)
    for line in f:
        searchObj = re.search( r'version(\s*)=(\s*)\'(.*)\'', line, re.M|re.I)
        if searchObj:
            versionName = searchObj.group(3) 
            break
    return versionName
 
if __name__ == "__main__":
 
    import shutil
    import sys
    import os
    content = "n"
 
    print sys.path[0]
    chdir(sys.path[0])
 
    import time    
    start_time = time.time()
    print ('start time %f' %start_time)
 
    # 开始读取参数
    if len(sys.argv) < 2:
        print "构建脚本 参数不够2个,第0个是脚本路径,第1个是git地址"
        exit()
 
    #项目名称
    OUTPUTS_PATH = OUTPUTS_PATH + sys.argv[1] + "/outputs"
 
    if (os.path.exists(OUTPUTS_PATH)):
        shutil.rmtree(OUTPUTS_PATH)
 
    # 代码
    git_path = sys.argv[2]
 
    # 第2个可以是分支
    if len(sys.argv) >= 4:
        BRANCH = sys.argv[3]
        pass
 
    print ('代码地址:' + git_path + ",分支:" + BRANCH)
 
 
    import re
    MODULE_NAME = re.search(r'/(.*)\.git', git_path).group(1)
 
    print ('项目名称:' + MODULE_NAME)
 
# clone代码
 
    SOURCE_CODE = MODULE_NAME;
 
    if os.path.exists(SOURCE_CODE):
        shutil.rmtree(SOURCE_CODE)
    os.mkdir(SOURCE_CODE)
    chdir(SOURCE_CODE)
    print ('start to clone codes')
 
    clone_command = "/usr/bin/git clone -b " + BRANCH + " " + git_path
 
    print clone_command
 
    print os.system(clone_command)
 
 
# 复制gradle配置文件
    chdir(os.path.dirname(os.getcwd()))
    print ('copy files from gradle_config to ' + SOURCE_CODE)
    copyFiles('../gradle_config', SOURCE_CODE)
 
 
# 进入library目录,开始编译
    chdir(SOURCE_CODE)
 
# 修改setting.gradle
 
    settingFile = open('settings.gradle', "wb")
    print settingFile.write("include '" + MODULE_NAME + "'")
    print settingFile.close()
 
    chdir(MODULE_NAME)
 
    print ('start to build library')
    output = os.popen(GRADL_HOME + "gradle clean build uploadArchives")
 
# 读取版本号
    versionName = getVersionName("build.gradle")
    print ("版本号为:" + versionName)
 
    buildResult = False
 
    outputLog = output.read()
 
    copyFiles("build/outputs", OUTPUTS_PATH)
 
    chdir(OUTPUTS_PATH)
 
    if "BUILD SUCCESSFUL" in outputLog:
        f = open( versionName + '.txt','w')
        f.write(outputLog)
        f.close()
        buildResult = True
        pass
 
    print outputLog
 
    end_time = time.time()
 
    cost_time  = (end_time - start_time)
    minutes = cost_time/60
    seconds = cost_time - 60 * minutes
    print ('cost time : %d mins %d secs' %(minutes, seconds))
 
    import sys
    if False == buildResult: 
        sys.exit(-1)
        pass

赏一个 您的支持是对我最大的鼓励。加班再忙,也要熬夜继续码字分享! 码字辛苦 赏个五毛 支持: 微信支付 支付宝

好人一生平安 谢谢!

微信支付 支付宝

用 [微信] 扫描二维码打赏

用 [支付宝] 扫描二维码打赏