java.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. import _common as common
  2. import ymake
  3. import json
  4. import os
  5. import base64
  6. import six
  7. DELIM = '================================'
  8. CONTRIB_JAVA_PREFIX = 'contrib/java/'
  9. def split_args(s): # TODO quotes, escapes
  10. return list(filter(None, s.split()))
  11. def extract_macro_calls(unit, macro_value_name, macro_calls_delim):
  12. if not unit.get(macro_value_name):
  13. return []
  14. return list(
  15. filter(
  16. None,
  17. map(split_args, unit.get(macro_value_name).replace('$' + macro_value_name, '').split(macro_calls_delim)),
  18. )
  19. )
  20. def extract_macro_calls2(unit, macro_value_name):
  21. if not unit.get(macro_value_name):
  22. return []
  23. calls = []
  24. for call_encoded_args in unit.get(macro_value_name).strip().split():
  25. call_args = json.loads(base64.b64decode(call_encoded_args))
  26. calls.append(call_args)
  27. return calls
  28. def onjava_module(unit, *args):
  29. args_delim = unit.get('ARGS_DELIM')
  30. if unit.get('YA_IDE_IDEA') != 'yes':
  31. return
  32. data = {
  33. 'BUNDLE_NAME': unit.name(),
  34. 'PATH': unit.path(),
  35. 'MODULE_TYPE': unit.get('MODULE_TYPE'),
  36. 'MODULE_ARGS': unit.get('MODULE_ARGS'),
  37. 'MANAGED_PEERS': '${MANAGED_PEERS}',
  38. 'MANAGED_PEERS_CLOSURE': '${MANAGED_PEERS_CLOSURE}',
  39. 'NON_NAMAGEABLE_PEERS': '${NON_NAMAGEABLE_PEERS}',
  40. 'JAVA_SRCS': extract_macro_calls(unit, 'JAVA_SRCS_VALUE', args_delim),
  41. 'JAVAC_FLAGS': extract_macro_calls(unit, 'JAVAC_FLAGS_VALUE', args_delim),
  42. 'ANNOTATION_PROCESSOR': extract_macro_calls(unit, 'ANNOTATION_PROCESSOR_VALUE', args_delim),
  43. # TODO remove when java test dart is in prod
  44. 'UNITTEST_DIR': unit.get('UNITTEST_DIR'),
  45. 'JVM_ARGS': extract_macro_calls(unit, 'JVM_ARGS_VALUE', args_delim),
  46. 'IDEA_EXCLUDE': extract_macro_calls(unit, 'IDEA_EXCLUDE_DIRS_VALUE', args_delim),
  47. 'IDEA_RESOURCE': extract_macro_calls(unit, 'IDEA_RESOURCE_DIRS_VALUE', args_delim),
  48. 'IDEA_MODULE_NAME': extract_macro_calls(unit, 'IDEA_MODULE_NAME_VALUE', args_delim),
  49. 'TEST_DATA': extract_macro_calls(unit, 'TEST_DATA_VALUE', args_delim),
  50. 'JDK_RESOURCE': 'JDK' + (unit.get('JDK_VERSION') or unit.get('JDK_REAL_VERSION') or '_DEFAULT'),
  51. }
  52. if unit.get('ENABLE_PREVIEW_VALUE') == 'yes' and (unit.get('JDK_VERSION') or unit.get('JDK_REAL_VERSION')) in (
  53. '17',
  54. '20',
  55. '21',
  56. '22',
  57. '23',
  58. ):
  59. data['ENABLE_PREVIEW'] = extract_macro_calls(unit, 'ENABLE_PREVIEW_VALUE', args_delim)
  60. if unit.get('SAVE_JAVAC_GENERATED_SRCS_DIR') and unit.get('SAVE_JAVAC_GENERATED_SRCS_TAR'):
  61. data['SAVE_JAVAC_GENERATED_SRCS_TAR'] = extract_macro_calls(unit, 'SAVE_JAVAC_GENERATED_SRCS_TAR', args_delim)
  62. if unit.get('JAVA_ADD_DLLS_VALUE') == 'yes':
  63. data['ADD_DLLS_FROM_DEPENDS'] = extract_macro_calls(unit, 'JAVA_ADD_DLLS_VALUE', args_delim)
  64. if unit.get('ERROR_PRONE_VALUE') == 'yes':
  65. data['ERROR_PRONE'] = extract_macro_calls(unit, 'ERROR_PRONE_VALUE', args_delim)
  66. if unit.get('WITH_KOTLIN_VALUE') == 'yes':
  67. if unit.get('KOTLINC_OPTS_VALUE'):
  68. data['KOTLINC_OPTS'] = extract_macro_calls(unit, 'KOTLINC_OPTS_VALUE', args_delim)
  69. if unit.get('JAVA_EXTERNAL_DEPENDENCIES_VALUE'):
  70. valid = []
  71. for dep in sum(extract_macro_calls(unit, 'JAVA_EXTERNAL_DEPENDENCIES_VALUE', args_delim), []):
  72. if os.path.normpath(dep).startswith('..'):
  73. ymake.report_configure_error(
  74. '{}: {} - relative paths in JAVA_EXTERNAL_DEPENDENCIES is not allowed'.format(unit.path(), dep)
  75. )
  76. elif os.path.isabs(dep):
  77. ymake.report_configure_error(
  78. '{}: {} absolute paths in JAVA_EXTERNAL_DEPENDENCIES is not allowed'.format(unit.path(), dep)
  79. )
  80. else:
  81. valid.append(dep)
  82. if valid:
  83. data['EXTERNAL_DEPENDENCIES'] = [valid]
  84. # IMPORTANT before switching vcs_info.py to python3 the value was always evaluated to $YMAKE_PYTHON but no
  85. # code in java dart parser extracts its value only checks this key for existance.
  86. data['EMBED_VCS'] = [['yes']]
  87. # FORCE_VCS_INFO_UPDATE is responsible for setting special value of VCS_INFO_DISABLE_CACHE__NO_UID__
  88. macro_val = extract_macro_calls(unit, 'FORCE_VCS_INFO_UPDATE', args_delim)
  89. macro_str = macro_val[0][0] if macro_val and macro_val[0] and macro_val[0][0] else ''
  90. if macro_str and macro_str == 'yes':
  91. data['VCS_INFO_DISABLE_CACHE__NO_UID__'] = macro_val
  92. for java_srcs_args in data['JAVA_SRCS']:
  93. external = None
  94. for i in six.moves.range(len(java_srcs_args)):
  95. arg = java_srcs_args[i]
  96. if arg == 'EXTERNAL':
  97. if not i + 1 < len(java_srcs_args):
  98. continue # TODO configure error
  99. ex = java_srcs_args[i + 1]
  100. if ex in ('EXTERNAL', 'SRCDIR', 'PACKAGE_PREFIX', 'EXCLUDE'):
  101. continue # TODO configure error
  102. if external is not None:
  103. continue # TODO configure error
  104. external = ex
  105. if external:
  106. unit.onpeerdir(external)
  107. data = {k: v for k, v in six.iteritems(data) if v}
  108. dart = 'JAVA_DART: ' + six.ensure_str(base64.b64encode(six.ensure_binary(json.dumps(data)))) + '\n' + DELIM + '\n'
  109. unit.set_property(['JAVA_DART_DATA', dart])
  110. def on_add_java_style_checks(unit, *args):
  111. if unit.get('LINT_LEVEL_VALUE') != "none" and common.get_no_lint_value(unit) != 'none':
  112. unit.onadd_check(['JAVA_STYLE', unit.get('LINT_LEVEL_VALUE')] + list(args))
  113. def on_add_kotlin_style_checks(unit, *args):
  114. """
  115. ktlint can be disabled using NO_LINT() and NO_LINT(ktlint)
  116. """
  117. if unit.get('WITH_KOTLIN_VALUE') == 'yes':
  118. if common.get_no_lint_value(unit) == '':
  119. unit.onadd_check(['ktlint'] + list(args))
  120. def on_add_classpath_clash_check(unit, *args):
  121. jdeps_val = (unit.get('CHECK_JAVA_DEPS_VALUE') or '').lower()
  122. if jdeps_val and jdeps_val not in ('yes', 'no', 'strict'):
  123. ymake.report_configure_error('CHECK_JAVA_DEPS: "yes", "no" or "strict" required')
  124. if jdeps_val and jdeps_val != 'no':
  125. unit.onjava_test_deps(jdeps_val)
  126. def on_add_detekt_report_check(unit, *args):
  127. if unit.get('WITH_KOTLIN_VALUE') == 'yes' and unit.get('WITH_KOTLINC_PLUGIN_DETEKT') == 'yes':
  128. unit.onadd_check(['detekt.report'] + list(args))
  129. # Ymake java modules related macros
  130. def on_check_java_srcdir(unit, *args):
  131. args = list(args)
  132. if 'SKIP_CHECK_SRCDIR' in args:
  133. return
  134. for arg in args:
  135. if '$' not in arg:
  136. arc_srcdir = os.path.join(unit.get('MODDIR'), arg)
  137. abs_srcdir = unit.resolve(os.path.join("$S/", arc_srcdir))
  138. if not os.path.exists(abs_srcdir) or not os.path.isdir(abs_srcdir):
  139. unit.onsrcdir(os.path.join('${ARCADIA_ROOT}', arc_srcdir))
  140. return
  141. srcdir = common.resolve_common_const(unit.resolve_arc_path(arg))
  142. if srcdir and srcdir.startswith('$S'):
  143. abs_srcdir = unit.resolve(srcdir)
  144. if not os.path.exists(abs_srcdir) or not os.path.isdir(abs_srcdir):
  145. unit.onsrcdir(os.path.join('${ARCADIA_ROOT}', srcdir[3:]))
  146. def on_fill_jar_copy_resources_cmd(unit, *args):
  147. if len(args) == 4:
  148. varname, srcdir, base_classes_dir, reslist = tuple(args)
  149. package = ''
  150. else:
  151. varname, srcdir, base_classes_dir, package, reslist = tuple(args)
  152. dest_dir = os.path.join(base_classes_dir, *package.split('.')) if package else base_classes_dir
  153. var = unit.get(varname)
  154. var += ' && $FS_TOOLS copy_files {} {} {}'.format(
  155. srcdir if srcdir.startswith('"$') else '${CURDIR}/' + srcdir, dest_dir, reslist
  156. )
  157. unit.set([varname, var])
  158. def on_fill_jar_gen_srcs(unit, *args):
  159. varname, jar_type, srcdir, base_classes_dir, java_list, kt_list, res_list = tuple(args[0:7])
  160. resolved_srcdir = unit.resolve_arc_path(srcdir)
  161. if not resolved_srcdir.startswith('$') or resolved_srcdir.startswith('$S'):
  162. return
  163. if jar_type == 'SRC_JAR' and unit.get('SOURCES_JAR') != 'yes':
  164. return
  165. args_delim = unit.get('JAR_BUILD_SCRIPT_FLAGS_DELIM')
  166. exclude_pos = args.index('EXCLUDE')
  167. globs = ' '.join(args[7:exclude_pos])
  168. excludes = ' '.join(args[exclude_pos + 1 :])
  169. var = unit.get(varname)
  170. var += f' {args_delim} --append -d {srcdir} -s {java_list} -k {kt_list} -r {res_list} --include-patterns {globs}'
  171. if jar_type == 'SRC_JAR':
  172. var += ' --all-resources'
  173. if len(excludes) > 0:
  174. var += f' --exclude-patterns {excludes}'
  175. if unit.get('WITH_KOTLIN_VALUE') == 'yes':
  176. var += ' --resolve-kotlin'
  177. unit.set([varname, var])
  178. def on_check_run_java_prog_classpath(unit, *args):
  179. if len(args) != 1:
  180. ymake.report_configure_error(
  181. 'multiple CLASSPATH elements in RUN_JAVA_PROGRAM invocation no more supported. Use JAVA_RUNTIME_PEERDIR on the JAVA_PROGRAM module instead'
  182. )
  183. def extract_words(words, keys):
  184. kv = {}
  185. k = None
  186. for w in words:
  187. if w in keys:
  188. k = w
  189. else:
  190. if k not in kv:
  191. kv[k] = []
  192. kv[k].append(w)
  193. return kv
  194. def parse_words(words):
  195. kv = extract_words(words, {'OUT', 'TEMPLATE'})
  196. if 'TEMPLATE' not in kv:
  197. kv['TEMPLATE'] = ['template.tmpl']
  198. ws = []
  199. for item in ('OUT', 'TEMPLATE'):
  200. for i, word in list(enumerate(kv[item])):
  201. if word == 'CUSTOM_PROPERTY':
  202. ws += kv[item][i:]
  203. kv[item] = kv[item][:i]
  204. templates = kv['TEMPLATE']
  205. outputs = kv['OUT']
  206. if len(outputs) < len(templates):
  207. ymake.report_configure_error('To many arguments for TEMPLATE parameter')
  208. return
  209. if ws and ws[0] != 'CUSTOM_PROPERTY':
  210. ymake.report_configure_error('''Can't parse {}'''.format(ws))
  211. custom_props = []
  212. for item in ws:
  213. if item == 'CUSTOM_PROPERTY':
  214. custom_props.append([])
  215. else:
  216. custom_props[-1].append(item)
  217. props = []
  218. for p in custom_props:
  219. if not p:
  220. ymake.report_configure_error('Empty CUSTOM_PROPERTY')
  221. continue
  222. props.append('-B')
  223. if len(p) > 1:
  224. props.append(six.ensure_str(base64.b64encode(six.ensure_binary("{}={}".format(p[0], ' '.join(p[1:]))))))
  225. else:
  226. ymake.report_configure_error('CUSTOM_PROPERTY "{}" value is not specified'.format(p[0]))
  227. for i, o in enumerate(outputs):
  228. yield o, templates[min(i, len(templates) - 1)], props
  229. def ongenerate_script(unit, *args):
  230. for out, tmpl, props in parse_words(list(args)):
  231. unit.on_add_gen_java_script([out, tmpl] + list(props))
  232. def on_jdk_version_macro_check(unit, *args):
  233. if len(args) != 1:
  234. unit.message(["error", "Invalid syntax. Single argument required."])
  235. jdk_version = args[0]
  236. available_versions = (
  237. '11',
  238. '17',
  239. '20',
  240. '21',
  241. '22',
  242. '23',
  243. )
  244. if jdk_version not in available_versions:
  245. ymake.report_configure_error(
  246. "Invalid jdk version: {}. {} are available".format(jdk_version, available_versions)
  247. )
  248. if int(jdk_version) >= 19 and unit.get('WITH_JDK_VALUE') != 'yes' and unit.get('MODULE_TAG') == 'JAR_RUNNABLE':
  249. msg = (
  250. "Missing WITH_JDK() macro for JDK version >= 19"
  251. # temporary link with additional explanation
  252. ". For more info see https://clubs.at.yandex-team.ru/arcadia/28543"
  253. )
  254. ymake.report_configure_error(msg)
  255. def _maven_coords_for_project(unit, project_dir):
  256. parts = project_dir.split('/')
  257. g = '.'.join(parts[2:-2])
  258. a = parts[-2]
  259. v = parts[-1]
  260. c = ''
  261. pom_path = unit.resolve(os.path.join('$S', project_dir, 'pom.xml'))
  262. if os.path.exists(pom_path):
  263. import xml.etree.ElementTree as et
  264. try:
  265. with open(pom_path, 'rb') as f:
  266. root = et.fromstring(f.read())
  267. for xpath in ('./{http://maven.apache.org/POM/4.0.0}artifactId', './artifactId'):
  268. artifact = root.find(xpath)
  269. if artifact is not None:
  270. artifact = artifact.text
  271. if a != artifact and a.startswith(artifact):
  272. c = a[len(artifact) :].lstrip('-_')
  273. a = artifact
  274. break
  275. except Exception as e:
  276. raise Exception(f"Can't parse {pom_path}: {str(e)}") from None
  277. return '{}:{}:{}:{}'.format(g, a, v, c)
  278. def on_setup_maven_export_coords_if_need(unit, *args):
  279. if not unit.enabled('MAVEN_EXPORT'):
  280. return
  281. unit.set(['MAVEN_EXPORT_COORDS_GLOBAL', _maven_coords_for_project(unit, args[0])])
  282. def _get_classpath(unit, dir):
  283. if dir.startswith(CONTRIB_JAVA_PREFIX):
  284. return '\\"{}\\"'.format(_maven_coords_for_project(unit, dir).rstrip(':'))
  285. else:
  286. return 'project(\\":{}\\")'.format(dir.replace('/', ':'))
  287. def on_setup_project_coords_if_needed(unit, *args):
  288. if not unit.enabled('EXPORT_GRADLE'):
  289. return
  290. project_dir = args[0]
  291. if project_dir.startswith(CONTRIB_JAVA_PREFIX):
  292. value = '{}'.format(_get_classpath(unit, project_dir).rstrip(':'))
  293. else:
  294. value = 'project(\\":{}\\")'.format(project_dir.replace('/', ':'))
  295. unit.set(['EXPORT_GRADLE_CLASSPATH', value])