generate_pom.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. from __future__ import print_function
  2. import sys
  3. import xml.etree.ElementTree as et
  4. import argparse
  5. import os
  6. import json
  7. import base64
  8. import re
  9. DEFAULT_YANDEX_GROUP_ID = 'ru.yandex'
  10. DEFAULT_NAMESPACE = 'http://maven.apache.org/POM/4.0.0'
  11. XSI_NAMESPACE = 'http://www.w3.org/2001/XMLSchema-instance'
  12. SCHEMA_LOCATION = 'http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd'
  13. MODEL_VERSION = '4.0.0'
  14. MAVEN_PLUGIN_GROUP_ID = 'org.apache.maven.plugins'
  15. MAVEN_PLUGIN_ARTIFACT_ID = 'maven-compiler-plugin'
  16. MAVEN_PLUGIN_VERSION = '3.3'
  17. JAVA_LANGUAGE_LEVEL = '1.8'
  18. MAVEN_BUILD_HELPER_GROUP_ID = 'org.codehaus.mojo'
  19. MAVEN_BUILD_HELPER_ARTIFACT_ID = 'build-helper-maven-plugin'
  20. MAVEN_BUILD_HELPER_VERSION = '1.9.1'
  21. MAVEN_EXEC_GROUP_ID = 'org.codehaus.mojo'
  22. MAVEN_EXEC_ARTIFACT_ID = 'exec-maven-plugin'
  23. MAVEN_EXEC_VERSION = '1.5.0'
  24. MAVEN_SUREFIRE_GROUP_ID = 'org.apache.maven.plugins'
  25. MAVEN_SUREFIRE_ARTIFACT_ID = 'maven-surefire-plugin'
  26. MAVEN_SUREFIRE_VERSION = '2.12.2'
  27. def target_from_contrib(target_path):
  28. return target_path.startswith('contrib')
  29. def split_artifacts(s):
  30. m = re.match('^([^:]*:[^:]*:[^:]*:[^:]*)(.*)$', s)
  31. if not m or not m.groups():
  32. return []
  33. if not m.groups()[1].startswith('::'):
  34. return [m.groups()[0]]
  35. return [m.groups()[0]] + m.groups()[1].split('::')[1:]
  36. def parse_coord_file(target_coords):
  37. deps = set()
  38. excludes = set()
  39. target = None
  40. with open(target_coords, 'r') as f:
  41. for ln in f:
  42. ln = ln.strip('\n')
  43. if ln.startswith('D='):
  44. if target is None:
  45. target = ln[2:]
  46. else:
  47. group, artifact = ln[2:].split(':')[0:2]
  48. deps.add('{}:{}'.format(group, artifact))
  49. elif ln.startswith('E='):
  50. group, artifact = ln[2:].split(':')[0:2]
  51. excludes.add('{}:{}'.format(group, artifact))
  52. return target, deps, excludes
  53. def applied_excludes(deps, excludes):
  54. return sorted(list(deps & excludes))
  55. def build_pom_and_export_to_maven(**kwargs):
  56. target_path = kwargs.get('target_path')
  57. target = kwargs.get('target')
  58. pom_path = kwargs.get('pom_path')
  59. source_dirs = kwargs.get('source_dirs')
  60. output_dir = kwargs.get('output_dir')
  61. final_name = kwargs.get('final_name')
  62. packaging = kwargs.get('packaging')
  63. test_target_dependencies = kwargs.get('test_target_dependencies')
  64. test_target_dependencies_exclude = kwargs.get('test_target_dependencies_exclude')
  65. modules_path = kwargs.get('modules_path')
  66. base64_prop_vars = kwargs.get('properties')
  67. prop_vars = kwargs.get('property')
  68. external_jars = kwargs.get('external_jars')
  69. resources = kwargs.get('resources')
  70. run_java_programs = [json.loads(base64.b64decode(i)) for i in kwargs.get('run_java_programs')]
  71. test_source_dirs = kwargs.get('test_source_dirs')
  72. test_resource_dirs = kwargs.get('test_resource_dirs')
  73. from_coord_files = kwargs.get('from_coord_files')
  74. deps_coords = kwargs.get('deps_coords')
  75. target_coords = kwargs.get('target_coords')
  76. if from_coord_files:
  77. target, _, all_excludes = parse_coord_file(target_coords)
  78. # TODO: ymake java -> jbuild java values format conversion must be removed
  79. target = ':'.join(target.split(':')[:3])
  80. target_dependencies = []
  81. for dep in deps_coords:
  82. dep_coord, dep_deps, _ = parse_coord_file(dep)
  83. excluded = applied_excludes(dep_deps, all_excludes)
  84. if len(excluded) > 0:
  85. target_dependencies.append('{}::{}'.format(dep_coord, '::'.join(excluded)))
  86. else:
  87. target_dependencies.append(dep_coord)
  88. else:
  89. target_dependencies = kwargs.get('target_dependencies')
  90. if kwargs.get('vcs_info') is not None:
  91. with open(kwargs.get('vcs_info'), 'r') as vcs_json:
  92. vcs_revision = json.load(vcs_json).get('ARCADIA_SOURCE_LAST_CHANGE')
  93. target = target.format(vcs_revision=vcs_revision)
  94. target_dependencies = [dep.format(vcs_revision=vcs_revision) for dep in target_dependencies]
  95. modules = []
  96. def _indent(elem, level=0):
  97. ind = "\n" + level * " "
  98. if len(elem):
  99. if not elem.text or not elem.text.strip():
  100. elem.text = ind + " "
  101. if not elem.tail or not elem.tail.strip():
  102. elem.tail = ind
  103. for elem in elem:
  104. _indent(elem, level + 1)
  105. if not elem.tail or not elem.tail.strip():
  106. elem.tail = ind
  107. else:
  108. if level and (not elem.tail or not elem.tail.strip()):
  109. elem.tail = ind
  110. project = et.Element(
  111. '{}{}{}project'.format('{', DEFAULT_NAMESPACE, '}'),
  112. attrib={'{}{}{}schemaLocation'.format('{', XSI_NAMESPACE, '}'): SCHEMA_LOCATION},
  113. )
  114. group_id, artifact_id, version = target.split(':')
  115. et.SubElement(project, 'modelVersion').text = MODEL_VERSION
  116. et.SubElement(project, 'groupId').text = group_id
  117. et.SubElement(project, 'artifactId').text = artifact_id
  118. et.SubElement(project, 'version').text = version
  119. et.SubElement(project, 'packaging').text = packaging
  120. properties = et.SubElement(project, 'properties')
  121. et.SubElement(properties, 'project.build.sourceEncoding').text = 'UTF-8'
  122. if base64_prop_vars:
  123. for property, value in json.loads(base64.b64decode(base64_prop_vars)).items():
  124. et.SubElement(properties, property).text = value
  125. for rawprop in prop_vars:
  126. property, sep, value = rawprop.partition('=')
  127. if sep != '=':
  128. print("Can't find propertyr name and property value in {}. No '=' symbol found".format(rawprop))
  129. sys.exit(1)
  130. et.SubElement(properties, property).text = value
  131. if modules_path:
  132. with open(modules_path) as f:
  133. modules = [i.strip() for i in f if i.strip()]
  134. if modules:
  135. modules_el = et.SubElement(project, 'modules')
  136. for module in modules:
  137. et.SubElement(modules_el, 'module').text = module
  138. build = et.SubElement(project, 'build')
  139. if source_dirs:
  140. et.SubElement(build, 'sourceDirectory').text = source_dirs[0]
  141. source_dirs = source_dirs[1:]
  142. if test_source_dirs:
  143. et.SubElement(build, 'testSourceDirectory').text = test_source_dirs[0]
  144. test_source_dirs = test_source_dirs[1:]
  145. if output_dir:
  146. et.SubElement(build, 'outputDirectory').text = output_dir
  147. if final_name:
  148. et.SubElement(build, 'finalName').text = final_name
  149. if resources:
  150. resource_element = et.SubElement(et.SubElement(build, 'resources'), 'resource')
  151. et.SubElement(resource_element, 'directory').text = '${basedir}'
  152. includes = et.SubElement(resource_element, 'includes')
  153. for resource in resources:
  154. et.SubElement(includes, 'include').text = resource
  155. if test_resource_dirs:
  156. test_resource_element = et.SubElement(build, 'testResources')
  157. for test_resource_dir in test_resource_dirs:
  158. et.SubElement(et.SubElement(test_resource_element, 'testResource'), 'directory').text = '${basedir}' + (
  159. ('/' + test_resource_dir) if test_resource_dir != '.' else ''
  160. )
  161. plugins = et.SubElement(build, 'plugins')
  162. if packaging != 'pom':
  163. maven_plugin = et.SubElement(plugins, 'plugin')
  164. et.SubElement(maven_plugin, 'groupId').text = MAVEN_PLUGIN_GROUP_ID
  165. et.SubElement(maven_plugin, 'artifactId').text = MAVEN_PLUGIN_ARTIFACT_ID
  166. et.SubElement(maven_plugin, 'version').text = MAVEN_PLUGIN_VERSION
  167. configuration = et.SubElement(maven_plugin, 'configuration')
  168. et.SubElement(configuration, 'source').text = JAVA_LANGUAGE_LEVEL
  169. et.SubElement(configuration, 'target').text = JAVA_LANGUAGE_LEVEL
  170. if source_dirs or external_jars or test_source_dirs:
  171. build_helper_plugin = et.SubElement(plugins, 'plugin')
  172. et.SubElement(build_helper_plugin, 'groupId').text = MAVEN_BUILD_HELPER_GROUP_ID
  173. et.SubElement(build_helper_plugin, 'artifactId').text = MAVEN_BUILD_HELPER_ARTIFACT_ID
  174. et.SubElement(build_helper_plugin, 'version').text = MAVEN_BUILD_HELPER_VERSION
  175. executions = et.SubElement(build_helper_plugin, 'executions')
  176. if source_dirs:
  177. execution = et.SubElement(executions, 'execution')
  178. et.SubElement(execution, 'id').text = 'add-source'
  179. et.SubElement(execution, 'phase').text = 'generate-sources'
  180. et.SubElement(et.SubElement(execution, 'goals'), 'goal').text = 'add-source'
  181. sources = et.SubElement(et.SubElement(execution, 'configuration'), 'sources')
  182. for source_dir in source_dirs:
  183. et.SubElement(sources, 'source').text = source_dir
  184. if external_jars:
  185. execution = et.SubElement(executions, 'execution')
  186. et.SubElement(execution, 'id').text = 'attach-artifacts'
  187. et.SubElement(execution, 'phase').text = 'generate-sources'
  188. et.SubElement(et.SubElement(execution, 'goals'), 'goal').text = 'attach-artifact'
  189. artifacts = et.SubElement(et.SubElement(execution, 'configuration'), 'artifacts')
  190. for external_jar in external_jars:
  191. external_artifact = et.SubElement(artifacts, 'artifact')
  192. et.SubElement(external_artifact, 'file').text = '${basedir}/' + external_jar
  193. et.SubElement(external_artifact, 'type').text = 'jar'
  194. if test_source_dirs:
  195. execution = et.SubElement(executions, 'execution')
  196. et.SubElement(execution, 'id').text = 'add-test-source'
  197. et.SubElement(execution, 'phase').text = 'generate-test-sources'
  198. et.SubElement(et.SubElement(execution, 'goals'), 'goal').text = 'add-test-source'
  199. sources = et.SubElement(et.SubElement(execution, 'configuration'), 'sources')
  200. for source_dir in source_dirs:
  201. et.SubElement(sources, 'source').text = source_dir
  202. if run_java_programs:
  203. exec_plugin = et.SubElement(plugins, 'plugin')
  204. et.SubElement(exec_plugin, 'groupId').text = MAVEN_EXEC_GROUP_ID
  205. et.SubElement(exec_plugin, 'artifactId').text = MAVEN_EXEC_ARTIFACT_ID
  206. et.SubElement(exec_plugin, 'version').text = MAVEN_EXEC_VERSION
  207. jp_dependencies = et.SubElement(exec_plugin, 'dependencies')
  208. executions = et.SubElement(exec_plugin, 'executions')
  209. for java_program in run_java_programs:
  210. execution = et.SubElement(executions, 'execution')
  211. et.SubElement(execution, 'phase').text = 'generate-sources'
  212. et.SubElement(et.SubElement(execution, 'goals'), 'goal').text = 'java'
  213. jp_configuration = et.SubElement(execution, 'configuration')
  214. main_cls, args = None, []
  215. for word in java_program['cmd']:
  216. if not main_cls and not word.startswith('-'):
  217. main_cls = word
  218. else:
  219. args.append(word)
  220. et.SubElement(jp_configuration, 'mainClass').text = main_cls
  221. et.SubElement(jp_configuration, 'includePluginDependencies').text = 'true'
  222. et.SubElement(jp_configuration, 'includeProjectDependencies').text = 'false'
  223. if args:
  224. jp_arguments = et.SubElement(jp_configuration, 'arguments')
  225. for arg in args:
  226. et.SubElement(jp_arguments, 'argument').text = arg
  227. if java_program['deps']:
  228. for jp_dep in java_program['deps']:
  229. jp_dependency = et.SubElement(jp_dependencies, 'dependency')
  230. jp_g, jp_a, jp_v = jp_dep.split(':')
  231. et.SubElement(jp_dependency, 'groupId').text = jp_g
  232. et.SubElement(jp_dependency, 'artifactId').text = jp_a
  233. et.SubElement(jp_dependency, 'version').text = jp_v
  234. et.SubElement(jp_dependency, 'type').text = 'jar'
  235. if target_dependencies + test_target_dependencies:
  236. dependencies = et.SubElement(project, 'dependencies')
  237. for target_dependency in target_dependencies + test_target_dependencies:
  238. dependency = et.SubElement(dependencies, 'dependency')
  239. dependency_info = split_artifacts(target_dependency)
  240. group_id, artifact_id, version, classifier = dependency_info[0].split(':')
  241. et.SubElement(dependency, 'groupId').text = group_id
  242. et.SubElement(dependency, 'artifactId').text = artifact_id
  243. et.SubElement(dependency, 'version').text = version
  244. if classifier:
  245. et.SubElement(dependency, 'classifier').text = classifier
  246. if target_dependency in test_target_dependencies:
  247. et.SubElement(dependency, 'scope').text = 'test'
  248. if len(dependency_info) > 1:
  249. exclusions = et.SubElement(dependency, 'exclusions')
  250. for exclude in dependency_info[1:]:
  251. group_id, artifact_id = exclude.split(':')
  252. exclusion_el = et.SubElement(exclusions, 'exclusion')
  253. et.SubElement(exclusion_el, 'groupId').text = group_id
  254. et.SubElement(exclusion_el, 'artifactId').text = artifact_id
  255. if test_target_dependencies_exclude:
  256. surefire_plugin = et.SubElement(plugins, 'plugin')
  257. et.SubElement(surefire_plugin, 'groupId').text = MAVEN_SUREFIRE_GROUP_ID
  258. et.SubElement(surefire_plugin, 'artifactId').text = MAVEN_SUREFIRE_ARTIFACT_ID
  259. et.SubElement(surefire_plugin, 'version').text = MAVEN_SUREFIRE_VERSION
  260. classpath_excludes = et.SubElement(
  261. et.SubElement(surefire_plugin, 'configuration'), 'classpathDependencyExcludes'
  262. )
  263. for classpath_exclude in test_target_dependencies_exclude:
  264. et.SubElement(classpath_excludes, 'classpathDependencyExclude').text = classpath_exclude
  265. et.register_namespace('', DEFAULT_NAMESPACE)
  266. et.register_namespace('xsi', XSI_NAMESPACE)
  267. _indent(project)
  268. et.ElementTree(project).write(pom_path)
  269. sys.stderr.write("[MAVEN EXPORT] Generated {} file for target {}\n".format(os.path.basename(pom_path), target_path))
  270. if __name__ == '__main__':
  271. parser = argparse.ArgumentParser()
  272. parser.add_argument('--from-coord-files', action='store_true')
  273. parser.add_argument('--deps-coords', action='append', default=[])
  274. parser.add_argument('--target-coords', action='store')
  275. parser.add_argument('--target-path', action='store', default='')
  276. parser.add_argument('--target', action='store')
  277. parser.add_argument('--pom-path', action='store')
  278. parser.add_argument('--source-dirs', action='append', default=[])
  279. parser.add_argument('--external-jars', action='append', default=[])
  280. parser.add_argument('--resources', action='append', default=[])
  281. parser.add_argument('--run-java-programs', action='append', default=[])
  282. parser.add_argument('--output-dir')
  283. parser.add_argument('--final-name')
  284. parser.add_argument('--packaging', default='jar')
  285. parser.add_argument('--target-dependencies', action='append', default=[])
  286. parser.add_argument('--test-target-dependencies', action='append', default=[])
  287. parser.add_argument('--test-target-dependencies-exclude', action='append', default=[])
  288. parser.add_argument('--modules-path', action='store')
  289. parser.add_argument('--properties')
  290. parser.add_argument('--property', action='append', default=[])
  291. parser.add_argument('--test-source-dirs', action='append', default=[])
  292. parser.add_argument('--test-resource-dirs', action='append', default=[])
  293. parser.add_argument('--vcs-info', action='store', default=None)
  294. args = parser.parse_args()
  295. build_pom_and_export_to_maven(**vars(args))