Android下的配置管理之道之高通拆仓 高通芯片平台的代码,一般可以分为android部分和amss部分。 android部分很简单就谷歌那一整套的代码的。 amss部分的代码属于高通自己的代码,算是私有代码。 不过这一部分代码仓库组织的很烂很烂。
amss这一部分 高通是把好多模块的代码分不同目录存放到一个git仓库里面了。导致这个git仓库十几GB的大小。 这个仓库的垃圾之处还在于有些不该加到git仓库里面的文件都加入了,例如一些编译生成的中间文件, 一些python执行的中间文件,每次编译这些文件都会被重新修改,真的很垃圾!!!shit~~~
再来说这个amss的仓库,如下列出了某个amss仓库的目录结构,基本上一个目录就是一个模块啦。 其中还有2个单独的文件about.html 和 contents.xml,这两个文件还是很重要的,about.html记录 高通每个模块的版本信息的,非常重要的。contents.xml和编译各个模块有关系,也是比较重要的一个文件。 其中还有一个比较特殊的目录LINUX/,这个是高通android侧的私有代码。应该放到android代码目录顶层 下面的vendor/qcom/proprietary目录下的。
鉴于以上种种原因,我们就需要对amss这个大仓库进行拆库操作了。
拆库有利用我们代码结构的整理,提交历史的整理。 每个模块单独一个仓库,研发提交可以不受影响各行其是。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 $ tree -L 1 -p -F . ├── [-rw-rw-r--] about.html ├── [drwxrwxr-x] adsp_proc/ ├── [drwxrwxr-x] aop_proc/ ├── [drwxrwxr-x] boot_images/ ├── [drwxrwxr-x] btfm_proc/ ├── [drwxrwxr-x] cdsp_proc/ ├── [drwxrwxr-x] common/ ├── [-rwxrwxr-x] contents.xml* ├── [drwxrwxr-x] LINUX/ ├── [drwxrwxr-x] modem_proc/ ├── [drwxrwxr-x] slpi_proc/ ├── [drwxrwxr-x] spss_proc/ ├── [drwxrwxr-x] trustzone_images/ ├── [drwxrwxr-x] venus_proc/ ├── [drwxrwxr-x] wdsp_proc/ ├── [drwxrwxr-x] wigig_proc/ └── [drwxrwxr-x] wlan_proc/ 15 directories, 2 files
通过上面我们基本上知道各个目录是上面了,也就知道该怎么拆库了。 基本上就是上面列出的每个目录拆库为一个小仓库,这些是amss侧各个模块的目录, LINUX/目录拆库为vendor/qcom/proprietary仓库。
下面给出命令
1 2 3 4 5 cd amss && git fetch --all --tags && git checkout r00455.2
git subtree 命令拆库,在amss仓库顶层目录执行
通过git命令帮助我们能够指定subtree有个split的子命令,split英文意思就是拆分的意思。
1 2 3 4 5 6 7 8 9 10 git-subtree - Merge subtrees together and split repository into subtrees git subtree add -P <prefix> <commit> git subtree add -P <prefix> <repository> <ref> git subtree pull -P <prefix> <repository> <ref> git subtree push -P <prefix> <repository> <ref> git subtree merge -P <prefix> <commit> git subtree split -P <prefix> [OPTIONS] [<commit>]
这里 -P 后面的需要拆库的目录, -b 后面是拆库后把这些代码分支提交存放到哪个分支上。(这个分支要是一个不存在的分支) 这里我们每个模块就取模块名称加上master组成我们的分支名称。
1 2 3 4 5 git subtree split -P trustzone_images -b trustzone_images_master
执行完上面的命令后,amss仓库下面就会多一个本地分支 trustzone_images_master 这个分支就包含了 目录 trustzone_images里面的所有代码,已经所有提交历史。
最后我们只需要把这个本地分支推送到gerrit代码服务器上对应的仓库就可以了
1 2 3 4 5 git push ssh://gerrit.com:29418/git/android/AMSS/trustzone_images trustzone_images_master:r00455.2 -f
这里我们amss侧的仓库都放到这样的路径下面 git/android/AMSS/。 推送到上面起个上面分支可以自己决定。一般 高通平台_r00455.2_rebase_日期 这样来命名这个分支。
其他的目录以此类推。
然后在给个 LINUX/ 拆库的命令
1 2 3 4 5 6 7 git subtree split -P LINUX/android/vendor/qcom/proprietary -b proprietary_master git push ssh://gerrit.com:29418/git/android/platform/vendor/qcom/proprietary proprietary_master:r00455.2 -f
这里LINUX/ 拆库的目录要到最里面的,不要值到LINUX/这一级别。要到LINUX/android/vendor/qcom/proprietary。 存放到gerrit上的路径也建议是 git/android/platform/vendor/qcom/proprietary。
git/android/ 下面都是我们代码仓库。当然不同公司可能组织代码仓库目录结构不太一样,有的可能开头叫个qcom/ ,或者有些就没有个开头的目录。
还是建议把所有的git仓库分分类,整理干净。
最后这个about.html 和 contents.xml 文件在单独提交一个patch到common仓库下面。
关于proprietary仓库,也可以再进行一次拆库,这个和amss的类似,就是每个子目录分别拆库放到不同的仓库下面。
最后能有个脚本自动,然后最后把拆库后的给个仓库是revision 信息打印一下,然后 组织的manifest也打印一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 trustzone_images ['9dc0fd4591ceffce056f38c71d702d80c6c184ab' , 'Commit label r00455.2 - Post-CS7 0.0.455.2' ] aop_proc ['06aaa952c6aa549dcc78874e3a791dc051229181' , 'Commit label r00455.2 - Post-CS7 0.0.455.2' ] spss_proc ['fed2fd8a54f97453195032f124835e0a9957b50f' , 'Commit label r00446.1 - Post-CS6 0.0.446.1' ] modem_proc ['331caebb7ab6b68f203fcd1633f272c92a436b78' , 'Commit label r00455.2.139522.1.139771.3 - Post-CS7 0.0.455.2.139522.1.139771.3' ] boot_images ['ea97b9aaa34c959eaf83e4c74f2bbc231a4e4b6c' , 'Commit label r00455.2 - Post-CS7 0.0.455.2' ] venus_proc ['c64930e9346a4de402ec05beb44bef89c0e34b65' , 'Commit label r00455.2 - Post-CS7 0.0.455.2' ] common ['028afc65f556c681cc7b597374065517a3b9632d' , 'Commit label r00455.2.139522.1.139771.3 - Post-CS7 0.0.455.2.139522.1.139771.3' ] slpi_proc ['0b8c16d09c40b5593ac7643282f3392ad2d12629' , 'Commit label r00455.2 - Post-CS7 0.0.455.2' ] LINUX ['9ed75e0dc096e7c7935134b9c444233005102e8f' , 'Commit label r00455.2.139522.1.139771.3 - Post-CS7 0.0.455.2.139522.1.139771.3' ] adsp_proc ['b2c789928e38fe434f8d9df87806d99d5effe3aa' , 'Commit label r00455.2 - Post-CS7 0.0.455.2' ] btfm_proc ['1280aa122fcd766ccf1bbfa1ea7eb1de25a38585' , 'Commit label r00455.2 - Post-CS7 0.0.455.2' ] wlan_proc ['674a8a55269bef51abb4e99f5ed3793cc0ca836c' , 'Commit label r00455.2.139522.1.139771.3 - Post-CS7 0.0.455.2.139522.1.139771.3' ] wigig_proc ['67dd08d91caad71284c475a53787bb9372e149e5' , 'Commit label r004361.1 - Post-CS5 0.0.4361.1' ] wdsp_proc ['775c398d1590cf18605f90e7fba2582743d1d73f' , 'Commit label r004361.1 - Post-CS5 0.0.4361.1' ] cdsp_proc ['47870d27ab8d7dff3e5e414cfef45138de2c5021' , 'Commit label r00455.2 - Post-CS7 0.0.455.2' ] <project name="AMSS/trustzone_images" path="AMSS/trustzone_images" revision="9dc0fd4591ceffce056f38c71d702d80c6c184ab" /> <project name="AMSS/aop_proc" path="AMSS/aop_proc" revision="06aaa952c6aa549dcc78874e3a791dc051229181" /> <project name="AMSS/spss_proc" path="AMSS/spss_proc" revision="fed2fd8a54f97453195032f124835e0a9957b50f" /> <project name="AMSS/modem_proc" path="AMSS/modem_proc" revision="331caebb7ab6b68f203fcd1633f272c92a436b78" /> <project name="AMSS/boot_images" path="AMSS/boot_images" revision="ea97b9aaa34c959eaf83e4c74f2bbc231a4e4b6c" /> <project name="AMSS/venus_proc" path="AMSS/venus_proc" revision="c64930e9346a4de402ec05beb44bef89c0e34b65" /> <project name="AMSS/common" path="AMSS/common" revision="028afc65f556c681cc7b597374065517a3b9632d" /> <project name="AMSS/slpi_proc" path="AMSS/slpi_proc" revision="0b8c16d09c40b5593ac7643282f3392ad2d12629" /> <project name="platform/vendor/qcom/proprietary" path="vendor/qcom/proprietary" revision="9ed75e0dc096e7c7935134b9c444233005102e8f" /> <project name="AMSS/adsp_proc" path="AMSS/adsp_proc" revision="b2c789928e38fe434f8d9df87806d99d5effe3aa" /> <project name="AMSS/btfm_proc" path="AMSS/btfm_proc" revision="1280aa122fcd766ccf1bbfa1ea7eb1de25a38585" /> <project name="AMSS/wlan_proc" path="AMSS/wlan_proc" revision="674a8a55269bef51abb4e99f5ed3793cc0ca836c" /> <project name="AMSS/wigig_proc" path="AMSS/wigig_proc" revision="67dd08d91caad71284c475a53787bb9372e149e5" /> <project name="AMSS/wdsp_proc" path="AMSS/wdsp_proc" revision="775c398d1590cf18605f90e7fba2582743d1d73f" /> <project name="AMSS/cdsp_proc" path="AMSS/cdsp_proc" revision="47870d27ab8d7dff3e5e414cfef45138de2c5021" />
下面给出一个python版本的拆库自动脚本,放到jenkins上面,填上参数,自动执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 import osimport commandsimport sysimport shutilimport optparsefrom aais.log import Logfrom aais.utils import Utilsclass SubtreeGitError (Exception ): pass class SubtreeGit (object ): def __init__ (self, basepath, srcbranch, targetpath, targetbranch ): dry_run = os.environ.get("DRY_RUN" , "true" ).strip() dry_run = True if dry_run == "true" else False self.dryrun = dry_run self.basepath = basepath self.srcbranch = srcbranch self.targetpath = targetpath self.targetbranch = targetbranch self.qcom_base = os.path.basename(basepath) self.proprietary_project = "git/android/platform/vendor/qcom/proprietary" self.gerrit_host = "gerrit.example.com" self.gerrit_port = "29418" def CloneGit (self ): if os.path.exists(self.qcom_base) and os.path.isdir(self.qcom_base): cmd = "cd %s && git fetch --all --tags && git checkout %s" % (self.qcom_base, self.srcbranch) else : cmd = "git clone ssh://%s:%s/%s %s && cd %s && git checkout %s" % (self.gerrit_host, self.gerrit_port, self.basepath, self.qcom_base, self.qcom_base, self.srcbranch) Log.Info("will clone/fetch base git and checkout to branch: %s, cmd: %s" % (self.srcbranch, cmd)) ret = os.system(cmd) if ret != 0 : raise SubtreeGitError("git clone/fetch/checkout fail" ) def GetRepertory (self, config="" ): if config == "" : repertory_L = os.listdir(self.qcom_base) repertory_L = [basedir.strip() for basedir in repertory_L if os.path.isdir(os.path.join(self.qcom_base, basedir)) and not basedir.startswith("." )] repertory_D = dict (zip (repertory_L, repertory_L)) return repertory_D else : repertory_D = Utils.StringToDict(config, firstreg=",|;" , sedondreg=":" ) return repertory_D def CreatePropject (self, targetpath, targetbranch, repertory_L ): cmd = "ssh -p %s %s gerrit ls-projects | grep -E \"%s|%s\" " % (self.gerrit_port, self.gerrit_host, targetpath, self.proprietary_project) Log.Info("list project on gerrit: %s" % cmd) status, output = commands.getstatusoutput(cmd) if status == 0 : project_L = output.splitlines() create_L = [] for repertory in repertory_L: project = os.path.join(targetpath, repertory) if repertory == "LINUX" or repertory == "proprietary" : project = self.proprietary_project if project not in project_L: create_L.append(project) for basedir in create_L: cmd = "ssh -p %s %s gerrit create-project %s --empty-commit --parent Permission_parent/All-bsp " \ "--submit-type REBASE_IF_NECESSARY --branch %s" % ( self.gerrit_port, self.gerrit_host, basedir, targetbranch) Log.Info("will create project: %s" % cmd) if not self.dryrun and os.system(cmd) != 0 : Log.Error("crate %s failed" % (basedir)) raise SubtreeGitError("create project fail" ) def PrintGitLog (self, repertory_L ): Log.Info("The repertory info list is:" ) manifest_str = "" for repertory in repertory_L: if repertory == "LINUX" or repertory == "proprietary" : log_cmd = "git log -1 -b proprietary_master --pretty=\"%H<gitlog>%s\"" else : local_branch = repertory.replace("/" , "__" ) log_cmd = "git log -1 -b %s_master --pretty=\"%%H<gitlog>%%s\"" % (local_branch) status, output = commands.getstatusoutput(log_cmd) if status == 0 : temp_L = output.strip().split("<gitlog>" ) Log.Red("%25s %s\n" % (repertory, temp_L)) git_commit_id = temp_L[0 ] if self.qcom_base == "proprietary" : manifest_str += " <project name=\"platform/vendor/qcom/proprietary/%s\" path=\"vendor/qcom/proprietary/%s\" revision=\"%s\" />\n" % ( repertory, repertory, git_commit_id) else : if repertory == "LINUX" or repertory == "proprietary" : manifest_str += " <project name=\"platform/vendor/qcom/proprietary\" path=\"vendor/qcom/proprietary\" revision=\"%s\" />\n" % ( git_commit_id) else : manifest_str +=" <project name=\"AMSS/%s\" path=\"AMSS/%s\" revision=\"%s\" />\n" % ( repertory, repertory, git_commit_id) Log.Blue("\n%s\n" % manifest_str) def SubtreeRepertory (self, config="" ): self.CloneGit() repertory_D = self.GetRepertory(config) self.CreatePropject(self.targetpath, self.targetbranch, repertory_D.keys()) os.chdir(self.qcom_base) cmd = 'git branch -D $(git for-each-ref --format="%(refname:short)" refs/heads/\*_master)' ret = os.system(cmd) if ret != 0 : Log.Error("delete branch fail" ) for (repertory, basedir) in repertory_D.items(): if repertory == "LINUX" or repertory == "proprietary" : if repertory == "LINUX" : basedir = "LINUX/android/vendor/qcom/proprietary" cmd = "git subtree split -P %s -b proprietary_master" % (basedir) Log.Info("subtree proprietary cmd: %s" % cmd) ret = os.system(cmd) if not self.dryrun and os.system(cmd) != 0 : raise SubtreeGitError("subtree split git fail" ) cmd = "git push ssh://%s:%s/%s proprietary_master:%s -f" % ( self.gerrit_host, self.gerrit_port, self.proprietary_project, self.targetbranch) Log.Info("will proprietary push to gerrit: %s" % cmd) if not self.dryrun and os.system(cmd) != 0 : raise SubtreeGitError("push proprietary to gerrit fail" ) else : local_branch = repertory.replace("/" , "__" ) cmd = "git subtree split -P %s -b %s_master" % (basedir, local_branch) Log.Info("subtree cmd: %s" % cmd) ret = os.system(cmd) if not self.dryrun and os.system(cmd) != 0 : raise SubtreeGitError("subtree split git fail" ) cmd = "git push ssh://%s:%s/%s/%s %s_master:%s -f" % ( self.gerrit_host, self.gerrit_port, self.targetpath, repertory, local_branch, self.targetbranch) Log.Info("will push to gerrit: %s" % cmd) if not self.dryrun and os.system(cmd) != 0 : raise SubtreeGitError("push to gerrit fail" ) self.PrintGitLog(repertory_D.keys()) def parseargs (): usage = "usage: [options] arg1 arg2" parser = optparse.OptionParser(usage=usage) optiongroup = optparse.OptionGroup(parser, "common options" ) optiongroup.add_option("" , "--base-path" , dest="basepath" , help ="git base path" , default="" ) optiongroup.add_option("" , "--target-path" , dest="targetpath" , help ="git push target path" , default="" ) optiongroup.add_option("" , "--src-branch" , dest="srcbranch" , help ="srcbranch" , default="" ) optiongroup.add_option("" , "--target-branch" , dest="targetbranch" , help ="targetbranch" , default="" ) optiongroup.add_option("" , "--config" , dest="config" , help ="config" , default="" ) parser.add_option_group(optiongroup) (options, args) = parser.parse_args() return (options, args) def main (): (options, args) = parseargs() basepath = options.basepath.strip() srcbranch = options.srcbranch.strip() targetpath = options.targetpath.strip() targetbranch = options.targetbranch.strip() config = options.config.strip() sub = SubtreeGit(basepath, srcbranch, targetpath, targetbranch) sub.SubtreeRepertory(config) if __name__ == "__main__" : sys.stdout = sys.stderr main()
jenkins 上面job里面写入下面的调用方式
1 2 3 4 5 6 7 8 9 10 #!/bin/bash -x export PYTHONPATH=aaispython tools/python/subtree_qcom.py \ --base-path "$BASE_PATH " --src-branch "$SRC_BRANCH " \ --target-path "$TARGET_PATH " --target-branch "$TARGET_BRANCH " \ --config "$CONFIG "
其中配置带参数的job,参数有
例如: AMSS地址为 git/shared/qcom/snapdragon-high-med-2016-spf-2-0_amss_standard_oem properity 为 git/android/platform/vendor/qcom/proprietary
这个地址会拼接成一个完整的url给git clone命令使用的。
高通的release tag点或者proprietary 仓库的revision值,用于git checkout
分仓之后的gerrit路径,就是最后要push到哪个project下面,主要是为AMSS提交地址做区分:
例如: AMSS 对应为 git/android/AMSS ,modem_proc 就会push到 git/android/AMSS/modem_proc下面。
proprietary 为 git/android/platform/vendor/qcom/proprietary。
填写的是project的 base path,不包括最后的 那个路径
分仓之后生成的分支名,分仓之后需要push到哪个分支下面 ,就是push到gerrit服务器上面起的分支名称。
默认为空。
但是有些amss仓库里面的子目录不正常,是多个平台共用的,跟正常的差太多了, 所以需要通过上传config来做特殊处理。后续遇到特殊情况,也可以用这个方法。
前面部分是仓库名,后面部分是地址。中间用冒号分隔,后面用分号,注意都是英文符号。
注意最后一行不要加分号,两行之间不要留空行
common:MSM8953.LA.2.0/Common;
proprietary:LA.UM.5.6/LINUX/android/vendor/qcom/proprietary;
adsp_proc:ADSP.8953.2.8.2/adsp_proc;
rpm_proc:RPM.BF.2.4/rpm_proc;
wcnss_proc:CNSS.PR.4.0/wcnss_proc;
cpe_proc:CPE.TSF.1.0/cpe_proc;
trustzone_images:TZ.BF.4.0.5/trustzone_images;
boot_images:BOOT.BF.3.3/boot_images;
modem_proc:MPSS.TA.2.3/modem_proc;
venus_proc:VIDEO.VE.4.2/venus_proc
config 如果填写 这个仓库一定是proprietary:LA.UM.5.6/LINUX/android/vendor/qcom/proprietary; 关键字一定是proprietary