gen_aar_gradle_script.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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.encoding = "UTF-8"
  168. failOnError false
  169. afterEvaluate {{
  170. def dependencyTree = project.configurations.compile.getAsFileTree()
  171. def aar_set = dependencyTree.matching{{include "**/*.aar"}}.getFiles()
  172. def jar_tree = dependencyTree.matching{{include "**/*.jar"}}
  173. classpath += files(android.libraryVariants.collect {{ libraryVariant ->
  174. libraryVariant.javaCompileProvider.get().classpath.files
  175. }})
  176. aar_set.each{{ aar ->
  177. def outputPath = "$buildDir/tmp/aarJar/${{aar.name.replace('.aar', '.jar')}}"
  178. classpath += files(outputPath)
  179. dependsOn task(name: "extract_${{aar.getAbsolutePath().replace(File.separatorChar, '_' as char)}}-${{capitalizedVariantName}}").doLast {{
  180. extractClassesJar(aar, outputPath)
  181. }}
  182. }}
  183. }}
  184. }}
  185. def packageJavadocTask = project.tasks.create(name: "package${{capitalizedVariantName}}Javadoc", type: Tar) {{
  186. description "Makes an archive from Javadoc output"
  187. from "${{buildDir}}/${{rootProject.name}}-javadoc/$capitalizedVariantName/"
  188. archiveFileName = "${{rootProject.name}}-javadoc.tar.gz"
  189. destinationDirectory = new File("${{buildDir}}")
  190. dependsOn javadocTask
  191. }}
  192. {enable_javadoc}
  193. }}
  194. }}
  195. private def extractClassesJar(aarPath, outputPath) {{
  196. if (!aarPath.exists()) {{
  197. throw new GradleException("AAR $aarPath not found")
  198. }}
  199. def zip = new ZipFile(aarPath)
  200. zip.entries().each {{
  201. if (it.name == "classes.jar") {{
  202. def path = Paths.get(outputPath)
  203. if (!Files.exists(path)) {{
  204. Files.createDirectories(path.getParent())
  205. Files.copy(zip.getInputStream(it), path)
  206. }}
  207. }}
  208. }}
  209. zip.close()
  210. }}
  211. """
  212. def gen_build_script(args):
  213. def wrap(items):
  214. return ',\n '.join('"{}"'.format(x) for x in items)
  215. bundles = []
  216. bundles_dirs = set(args.flat_repos)
  217. for bundle in args.bundles:
  218. dir_name, base_name = os.path.split(bundle)
  219. assert(len(dir_name) > 0 and len(base_name) > 0)
  220. name, ext = os.path.splitext(base_name)
  221. assert(len(name) > 0 and ext == '.aar')
  222. bundles_dirs.add(dir_name)
  223. bundles.append('com.yandex:{}@aar'.format(name))
  224. if len(bundles_dirs) > 0:
  225. flat_dirs_repo = FLAT_DIRS_REPO_TEMPLATE.format(dirs=wrap(bundles_dirs))
  226. else:
  227. flat_dirs_repo = ''
  228. maven_repos = ''.join(MAVEN_REPO_TEMPLATE.format(repo=repo) for repo in args.maven_repos)
  229. if args.keystore:
  230. keystore = KEYSTORE_TEMLATE.format(keystore=args.keystore)
  231. else:
  232. keystore = ''
  233. if args.generate_doc:
  234. enable_javadoc = ENABLE_JAVADOC
  235. else:
  236. enable_javadoc = ''
  237. if args.do_not_strip:
  238. do_not_strip = DO_NOT_STRIP
  239. else:
  240. do_not_strip = ''
  241. return AAR_TEMPLATE.format(
  242. aars=wrap(args.aars),
  243. compile_only_aars=wrap(args.compile_only_aars),
  244. aidl_dirs=wrap(args.aidl_dirs),
  245. assets_dirs=wrap(args.assets_dirs),
  246. bundles=wrap(bundles),
  247. do_not_strip=do_not_strip,
  248. enable_javadoc=enable_javadoc,
  249. flat_dirs_repo=flat_dirs_repo,
  250. java_dirs=wrap(args.java_dirs),
  251. jni_libs_dirs=wrap(args.jni_libs_dirs),
  252. keystore=keystore,
  253. manifest=args.manifest,
  254. maven_repos=maven_repos,
  255. proguard_rules=args.proguard_rules,
  256. res_dirs=wrap(args.res_dirs),
  257. )
  258. if __name__ == '__main__':
  259. parser = argparse.ArgumentParser()
  260. parser.add_argument('--aars', nargs='*', default=[])
  261. parser.add_argument('--compile-only-aars', nargs='*', default=[])
  262. parser.add_argument('--aidl-dirs', nargs='*', default=[])
  263. parser.add_argument('--assets-dirs', nargs='*', default=[])
  264. parser.add_argument('--bundle-name', nargs='?', default='default-bundle-name')
  265. parser.add_argument('--bundles', nargs='*', default=[])
  266. parser.add_argument('--do-not-strip', action='store_true')
  267. parser.add_argument('--flat-repos', nargs='*', default=[])
  268. parser.add_argument('--generate-doc', action='store_true')
  269. parser.add_argument('--java-dirs', nargs='*', default=[])
  270. parser.add_argument('--jni-libs-dirs', nargs='*', default=[])
  271. parser.add_argument('--keystore', default=None)
  272. parser.add_argument('--manifest', required=True)
  273. parser.add_argument('--maven-repos', nargs='*', default=[])
  274. parser.add_argument('--output-dir', required=True)
  275. parser.add_argument('--peers', nargs='*', default=[])
  276. parser.add_argument('--proguard-rules', nargs='?', default=None)
  277. parser.add_argument('--res-dirs', nargs='*', default=[])
  278. args = parser.parse_args()
  279. if args.proguard_rules is None:
  280. args.proguard_rules = os.path.join(args.output_dir, 'proguard-rules.txt')
  281. with open(args.proguard_rules, 'w') as f:
  282. pass
  283. for index, jsrc in enumerate(filter(lambda x: x.endswith('.jsrc'), args.peers)):
  284. jsrc_dir = os.path.join(args.output_dir, 'jsrc_{}'.format(str(index)))
  285. os.makedirs(jsrc_dir)
  286. with tarfile.open(jsrc, 'r') as tar:
  287. tar.extractall(path=jsrc_dir)
  288. args.java_dirs.append(jsrc_dir)
  289. args.build_gradle = os.path.join(args.output_dir, 'build.gradle')
  290. args.settings_gradle = os.path.join(args.output_dir, 'settings.gradle')
  291. args.gradle_properties = os.path.join(args.output_dir, 'gradle.properties')
  292. content = gen_build_script(args)
  293. with open(args.build_gradle, 'w') as f:
  294. f.write(content)
  295. with open(args.gradle_properties, 'w') as f:
  296. f.write('android.useAndroidX=true')
  297. if args.bundle_name:
  298. with open(args.settings_gradle, 'w') as f:
  299. f.write('rootProject.name = "{}"'.format(args.bundle_name))