gen_aar_gradle_script.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. import argparse
  2. import os
  3. import tarfile
  4. FLAT_DIRS_REPO_TEMPLATE='flatDir {{ dirs {dirs} }}\n'
  5. MAVEN_REPO_TEMPLATE='maven {{ url "{repo}" }}\n'
  6. KEYSTORE_TEMLATE='signingConfigs {{ debug {{ storeFile file("{keystore}") }} }}\n'
  7. ENABLE_JAVADOC = 'tasks["bundle${suffix}Aar"].dependsOn packageJavadocTask'
  8. DO_NOT_STRIP = '''\
  9. packagingOptions {
  10. doNotStrip "*/arm64-v8a/*.so"
  11. doNotStrip "*/armeabi-v7a/*.so"
  12. doNotStrip "*/x86_64/*.so"
  13. doNotStrip "*/x86/*.so"
  14. }
  15. '''
  16. AAR_TEMPLATE = """\
  17. ext.jniLibsDirs = [
  18. {jni_libs_dirs}
  19. ]
  20. ext.resDirs = [
  21. {res_dirs}
  22. ]
  23. ext.assetsDirs = [
  24. {assets_dirs}
  25. ]
  26. ext.javaDirs = [
  27. {java_dirs}
  28. ]
  29. def aidlDirs = [
  30. {aidl_dirs}
  31. ]
  32. ext.bundles = [
  33. {bundles}
  34. ]
  35. ext.androidArs = [
  36. {aars}
  37. ]
  38. ext.compileOnlyAndroidArs = [
  39. {compile_only_aars}
  40. ]
  41. def minVersion = 21
  42. def compileVersion = 30
  43. def targetVersion = 30
  44. def buildVersion = '30.0.3'
  45. import com.android.build.gradle.LibraryPlugin
  46. import java.nio.file.Files
  47. import java.nio.file.Paths
  48. import java.util.regex.Matcher
  49. import java.util.regex.Pattern
  50. import java.util.zip.ZipFile
  51. apply plugin: 'com.github.dcendents.android-maven'
  52. buildDir = "$projectDir/build"
  53. if (!ext.has("packageSuffix"))
  54. ext.packageSuffix = ""
  55. buildscript {{
  56. // repositories {{
  57. // jcenter()
  58. // mavenCentral()
  59. // }}
  60. repositories {{
  61. {maven_repos}
  62. }}
  63. dependencies {{
  64. classpath 'com.android.tools.build:gradle:4.0.2'
  65. classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
  66. }}
  67. }}
  68. apply plugin: LibraryPlugin
  69. repositories {{
  70. // flatDir {{
  71. // dirs System.env.PKG_ROOT + '/bundle'
  72. // }}
  73. // maven {{
  74. // url "http://maven.google.com/"
  75. // }}
  76. // maven {{
  77. // url "http://artifactory.yandex.net/artifactory/public/"
  78. // }}
  79. {flat_dirs_repo}
  80. {maven_repos}
  81. }}
  82. android {{
  83. {keystore}
  84. compileSdkVersion compileVersion
  85. buildToolsVersion buildVersion
  86. defaultConfig {{
  87. minSdkVersion minVersion
  88. targetSdkVersion targetVersion
  89. consumerProguardFiles '{proguard_rules}'
  90. }}
  91. sourceSets {{
  92. main {{
  93. manifest.srcFile '{manifest}'
  94. jniLibs.srcDirs = jniLibsDirs
  95. res.srcDirs = resDirs
  96. assets.srcDirs = assetsDirs
  97. java.srcDirs = javaDirs
  98. aidl.srcDirs = aidlDirs
  99. }}
  100. // We don't use this feature, so we set it to nonexisting directory
  101. androidTest.setRoot('bundle/tests')
  102. }}
  103. {do_not_strip}
  104. dependencies {{
  105. for (bundle in bundles)
  106. compile("$bundle") {{
  107. transitive = true
  108. }}
  109. for (bundle in androidArs)
  110. compile(bundle) {{
  111. transitive = true
  112. }}
  113. for (bundle in compileOnlyAndroidArs)
  114. compileOnly(bundle)
  115. }}
  116. android.libraryVariants.all {{ variant ->
  117. def suffix = variant.buildType.name.capitalize()
  118. def sourcesJarTask = project.tasks.create(name: "sourcesJar${{suffix}}", type: Jar) {{
  119. classifier = 'sources'
  120. from android.sourceSets.main.java.srcDirs
  121. include '**/*.java'
  122. eachFile {{ fcd ->
  123. def segments = fcd.relativePath.segments
  124. if (segments[0] == 'impl') {{
  125. fcd.relativePath = new RelativePath(true, segments.drop(1))
  126. }}
  127. }}
  128. includeEmptyDirs = false
  129. }}
  130. def manifestFile = android.sourceSets.main.manifest.srcFile
  131. def manifestXml = new XmlParser().parse(manifestFile)
  132. def packageName = manifestXml['@package']
  133. def groupName = packageName.tokenize('.')[0..-2].join('.')
  134. def androidNs = new groovy.xml.Namespace("http://schemas.android.com/apk/res/android")
  135. def packageVersion = manifestXml.attributes()[androidNs.versionName]
  136. def writePomTask = project.tasks.create(name: "writePom${{suffix}}") {{
  137. pom {{
  138. project {{
  139. groupId groupName
  140. version packageVersion
  141. packaging 'aar'
  142. }}
  143. }}.writeTo("$buildDir/${{rootProject.name}}$packageSuffix-pom.xml")
  144. }}
  145. tasks["bundle${{suffix}}Aar"].dependsOn sourcesJarTask
  146. tasks["bundle${{suffix}}Aar"].dependsOn writePomTask
  147. }}
  148. android.libraryVariants.all {{ variant ->
  149. def capitalizedVariantName = variant.name.capitalize()
  150. def suffix = variant.buildType.name.capitalize()
  151. def javadocTask = project.tasks.create(name: "generate${{capitalizedVariantName}}Javadoc", type: Javadoc) {{
  152. group = "Javadoc"
  153. description "Generates Javadoc for $capitalizedVariantName"
  154. title = "Yandex documentation"
  155. source = android.sourceSets.main.java.srcDirs
  156. include "**/*/yandex/*/**"
  157. // TODO: remove this when we support internal doc exclusion in IDL
  158. // https://st.yandex-team.ru/MAPSMOBCORE-11364
  159. exclude "**/internal/**"
  160. ext.androidJar = "${{android.sdkDirectory.path}}/platforms/${{android.compileSdkVersion}}/android.jar"
  161. classpath =
  162. files(android.getBootClasspath().join(File.pathSeparator)) +
  163. configurations.compile +
  164. files(ext.androidJar) +
  165. files(variant.javaCompile.outputs.files)
  166. destinationDir = file("$buildDir/${{rootProject.name}}-javadoc/$capitalizedVariantName/")
  167. options.doclet("ExcludeDoclet")
  168. options.docletpath(
  169. files(repositories.maven.url).getAsFileTree()
  170. .matching{{include "**/exclude-doclet-1.0.0.jar"}}
  171. .getSingleFile())
  172. options.charSet = "UTF-8"
  173. options.encoding = "UTF-8"
  174. failOnError false
  175. afterEvaluate {{
  176. def dependencyTree = project.configurations.compile.getAsFileTree()
  177. def aar_set = dependencyTree.matching{{include "**/*.aar"}}.getFiles()
  178. def jar_tree = dependencyTree.matching{{include "**/*.jar"}}
  179. aar_set.each{{ aar ->
  180. def outputPath = "$buildDir/tmp/aarJar/${{aar.name.replace('.aar', '.jar')}}"
  181. classpath += files(outputPath)
  182. dependsOn task(name: "extract_${{aar.getAbsolutePath().replace(File.separatorChar, '_' as char)}}-${{capitalizedVariantName}}").doLast {{
  183. extractClassesJar(aar, outputPath)
  184. }}
  185. }}
  186. }}
  187. }}
  188. def packageJavadocTask = project.tasks.create(name: "package${{capitalizedVariantName}}Javadoc", type: Tar) {{
  189. description "Makes an archive from Javadoc output"
  190. from "${{buildDir}}/${{rootProject.name}}-javadoc/$capitalizedVariantName/"
  191. archiveFileName = "${{rootProject.name}}-javadoc.tar.gz"
  192. destinationDirectory = new File("${{buildDir}}")
  193. dependsOn javadocTask
  194. }}
  195. {enable_javadoc}
  196. }}
  197. }}
  198. private def extractClassesJar(aarPath, outputPath) {{
  199. if (!aarPath.exists()) {{
  200. throw new GradleException("AAR $aarPath not found")
  201. }}
  202. def zip = new ZipFile(aarPath)
  203. zip.entries().each {{
  204. if (it.name == "classes.jar") {{
  205. def path = Paths.get(outputPath)
  206. if (!Files.exists(path)) {{
  207. Files.createDirectories(path.getParent())
  208. Files.copy(zip.getInputStream(it), path)
  209. }}
  210. }}
  211. }}
  212. zip.close()
  213. }}
  214. """
  215. def gen_build_script(args):
  216. def wrap(items):
  217. return ',\n '.join('"{}"'.format(x) for x in items)
  218. bundles = []
  219. bundles_dirs = set(args.flat_repos)
  220. for bundle in args.bundles:
  221. dir_name, base_name = os.path.split(bundle)
  222. assert(len(dir_name) > 0 and len(base_name) > 0)
  223. name, ext = os.path.splitext(base_name)
  224. assert(len(name) > 0 and ext == '.aar')
  225. bundles_dirs.add(dir_name)
  226. bundles.append('com.yandex:{}@aar'.format(name))
  227. if len(bundles_dirs) > 0:
  228. flat_dirs_repo = FLAT_DIRS_REPO_TEMPLATE.format(dirs=wrap(bundles_dirs))
  229. else:
  230. flat_dirs_repo = ''
  231. maven_repos = ''.join(MAVEN_REPO_TEMPLATE.format(repo=repo) for repo in args.maven_repos)
  232. if args.keystore:
  233. keystore = KEYSTORE_TEMLATE.format(keystore=args.keystore)
  234. else:
  235. keystore = ''
  236. if args.generate_doc:
  237. enable_javadoc = ENABLE_JAVADOC
  238. else:
  239. enable_javadoc = ''
  240. if args.do_not_strip:
  241. do_not_strip = DO_NOT_STRIP
  242. else:
  243. do_not_strip = ''
  244. return AAR_TEMPLATE.format(
  245. aars=wrap(args.aars),
  246. compile_only_aars=wrap(args.compile_only_aars),
  247. aidl_dirs=wrap(args.aidl_dirs),
  248. assets_dirs=wrap(args.assets_dirs),
  249. bundles=wrap(bundles),
  250. do_not_strip=do_not_strip,
  251. enable_javadoc=enable_javadoc,
  252. flat_dirs_repo=flat_dirs_repo,
  253. java_dirs=wrap(args.java_dirs),
  254. jni_libs_dirs=wrap(args.jni_libs_dirs),
  255. keystore=keystore,
  256. manifest=args.manifest,
  257. maven_repos=maven_repos,
  258. proguard_rules=args.proguard_rules,
  259. res_dirs=wrap(args.res_dirs),
  260. )
  261. if __name__ == '__main__':
  262. parser = argparse.ArgumentParser()
  263. parser.add_argument('--aars', nargs='*', default=[])
  264. parser.add_argument('--compile-only-aars', nargs='*', default=[])
  265. parser.add_argument('--aidl-dirs', nargs='*', default=[])
  266. parser.add_argument('--assets-dirs', nargs='*', default=[])
  267. parser.add_argument('--bundle-name', nargs='?', default='default-bundle-name')
  268. parser.add_argument('--bundles', nargs='*', default=[])
  269. parser.add_argument('--do-not-strip', action='store_true')
  270. parser.add_argument('--flat-repos', nargs='*', default=[])
  271. parser.add_argument('--generate-doc', action='store_true')
  272. parser.add_argument('--java-dirs', nargs='*', default=[])
  273. parser.add_argument('--jni-libs-dirs', nargs='*', default=[])
  274. parser.add_argument('--keystore', default=None)
  275. parser.add_argument('--manifest', required=True)
  276. parser.add_argument('--maven-repos', nargs='*', default=[])
  277. parser.add_argument('--output-dir', required=True)
  278. parser.add_argument('--peers', nargs='*', default=[])
  279. parser.add_argument('--proguard-rules', nargs='?', default=None)
  280. parser.add_argument('--res-dirs', nargs='*', default=[])
  281. args = parser.parse_args()
  282. if args.proguard_rules is None:
  283. args.proguard_rules = os.path.join(args.output_dir, 'proguard-rules.txt')
  284. with open(args.proguard_rules, 'w') as f:
  285. pass
  286. for index, jsrc in enumerate(filter(lambda x: x.endswith('.jsrc'), args.peers)):
  287. jsrc_dir = os.path.join(args.output_dir, 'jsrc_{}'.format(str(index)))
  288. os.makedirs(jsrc_dir)
  289. with tarfile.open(jsrc, 'r') as tar:
  290. tar.extractall(path=jsrc_dir)
  291. args.java_dirs.append(jsrc_dir)
  292. args.build_gradle = os.path.join(args.output_dir, 'build.gradle')
  293. args.settings_gradle = os.path.join(args.output_dir, 'settings.gradle')
  294. args.gradle_properties = os.path.join(args.output_dir, 'gradle.properties')
  295. content = gen_build_script(args)
  296. with open(args.build_gradle, 'w') as f:
  297. f.write(content)
  298. with open(args.gradle_properties, 'w') as f:
  299. f.write('android.useAndroidX=true')
  300. if args.bundle_name:
  301. with open(args.settings_gradle, 'w') as f:
  302. f.write('rootProject.name = "{}"'.format(args.bundle_name))