java.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. import _common as common
  2. import ymake
  3. import json
  4. import os
  5. import base64
  6. DELIM = '================================'
  7. def split_args(s): # TODO quotes, escapes
  8. return filter(None, s.split())
  9. def extract_macro_calls(unit, macro_value_name, macro_calls_delim):
  10. if not unit.get(macro_value_name):
  11. return []
  12. return filter(None, map(split_args, unit.get(macro_value_name).replace('$' + macro_value_name, '').split(macro_calls_delim)))
  13. def extract_macro_calls2(unit, macro_value_name):
  14. if not unit.get(macro_value_name):
  15. return []
  16. calls = []
  17. for call_encoded_args in unit.get(macro_value_name).strip().split():
  18. call_args = json.loads(base64.b64decode(call_encoded_args), encoding='utf-8')
  19. calls.append(call_args)
  20. return calls
  21. def on_run_jbuild_program(unit, *args):
  22. args = list(args)
  23. """
  24. Custom code generation
  25. @link: https://wiki.yandex-team.ru/yatool/java/#kodogeneracijarunjavaprogram
  26. """
  27. flat, kv = common.sort_by_keywords({'IN': -1, 'IN_DIR': -1, 'OUT': -1, 'OUT_DIR': -1, 'CWD': 1, 'CLASSPATH': -1, 'CP_USE_COMMAND_FILE': 1, 'ADD_SRCS_TO_CLASSPATH': 0}, args)
  28. depends = kv.get('CLASSPATH', []) + kv.get('JAR', [])
  29. fake_out = None
  30. if depends:
  31. # XXX: hack to force ymake to build dependencies
  32. fake_out = "fake.out.{}".format(hash(tuple(args)))
  33. unit.on_run_java(['TOOL'] + depends + ["OUT", fake_out])
  34. if not kv.get('CP_USE_COMMAND_FILE'):
  35. args += ['CP_USE_COMMAND_FILE', unit.get(['JAVA_PROGRAM_CP_USE_COMMAND_FILE']) or 'yes']
  36. if fake_out is not None:
  37. args += ['FAKE_OUT', fake_out]
  38. prev = unit.get(['RUN_JAVA_PROGRAM_VALUE']) or ''
  39. new_val = (prev + ' ' + base64.b64encode(json.dumps(list(args), encoding='utf-8'))).strip()
  40. unit.set(['RUN_JAVA_PROGRAM_VALUE', new_val])
  41. def ongenerate_script(unit, *args):
  42. """
  43. heretic@ promised to make tutorial here
  44. Don't forget
  45. Feel free to remind
  46. """
  47. flat, kv = common.sort_by_keywords(
  48. {'OUT': -1, 'TEMPLATE': -1, 'CUSTOM_PROPERTY': -1},
  49. args
  50. )
  51. if len(kv.get('TEMPLATE', [])) > len(kv.get('OUT', [])):
  52. ymake.report_configure_error('To many arguments for TEMPLATE parameter')
  53. prev = unit.get(['GENERATE_SCRIPT_VALUE']) or ''
  54. new_val = (prev + ' ' + base64.b64encode(json.dumps(list(args), encoding='utf-8'))).strip()
  55. unit.set(['GENERATE_SCRIPT_VALUE', new_val])
  56. def onjava_module(unit, *args):
  57. args_delim = unit.get('ARGS_DELIM')
  58. idea_only = True if 'IDEA_ONLY' in args else False
  59. if idea_only:
  60. if unit.get('YA_IDE_IDEA') != 'yes':
  61. return
  62. if unit.get('YMAKE_JAVA_MODULES') != 'yes':
  63. return
  64. data = {
  65. 'BUNDLE_NAME': unit.name(),
  66. 'PATH': unit.path(),
  67. 'IDEA_ONLY': 'yes' if idea_only else 'no',
  68. 'MODULE_TYPE': unit.get('MODULE_TYPE'),
  69. 'MODULE_ARGS': unit.get('MODULE_ARGS'),
  70. 'MANAGED_PEERS': '${MANAGED_PEERS}',
  71. 'MANAGED_PEERS_CLOSURE': '${MANAGED_PEERS_CLOSURE}',
  72. 'NON_NAMAGEABLE_PEERS': '${NON_NAMAGEABLE_PEERS}',
  73. 'TEST_CLASSPATH_MANAGED': '${TEST_CLASSPATH_MANAGED}',
  74. 'EXCLUDE': extract_macro_calls(unit, 'EXCLUDE_VALUE', args_delim),
  75. 'JAVA_SRCS': extract_macro_calls(unit, 'JAVA_SRCS_VALUE', args_delim),
  76. 'JAVAC_FLAGS': extract_macro_calls(unit, 'JAVAC_FLAGS_VALUE', args_delim),
  77. 'ANNOTATION_PROCESSOR': extract_macro_calls(unit, 'ANNOTATION_PROCESSOR_VALUE', args_delim),
  78. 'EXTERNAL_JAR': extract_macro_calls(unit, 'EXTERNAL_JAR_VALUE', args_delim),
  79. 'RUN_JAVA_PROGRAM': extract_macro_calls2(unit, 'RUN_JAVA_PROGRAM_VALUE'),
  80. 'RUN_JAVA_PROGRAM_MANAGED': '${RUN_JAVA_PROGRAM_MANAGED}',
  81. 'MAVEN_GROUP_ID': extract_macro_calls(unit, 'MAVEN_GROUP_ID_VALUE', args_delim),
  82. 'JAR_INCLUDE_FILTER': extract_macro_calls(unit, 'JAR_INCLUDE_FILTER_VALUE', args_delim),
  83. 'JAR_EXCLUDE_FILTER': extract_macro_calls(unit, 'JAR_EXCLUDE_FILTER_VALUE', args_delim),
  84. # TODO remove when java test dart is in prod
  85. 'UNITTEST_DIR': unit.get('UNITTEST_DIR'),
  86. 'SYSTEM_PROPERTIES': extract_macro_calls(unit, 'SYSTEM_PROPERTIES_VALUE', args_delim),
  87. 'JVM_ARGS': extract_macro_calls(unit, 'JVM_ARGS_VALUE', args_delim),
  88. 'TEST_CWD': extract_macro_calls(unit, 'TEST_CWD_VALUE', args_delim),
  89. 'TEST_FORK_MODE': extract_macro_calls(unit, 'TEST_FORK_MODE', args_delim),
  90. 'SPLIT_FACTOR': extract_macro_calls(unit, 'TEST_SPLIT_FACTOR', args_delim),
  91. 'TIMEOUT': extract_macro_calls(unit, 'TEST_TIMEOUT', args_delim),
  92. 'TAG': extract_macro_calls(unit, 'TEST_TAGS_VALUE', args_delim),
  93. 'SIZE': extract_macro_calls(unit, 'TEST_SIZE_NAME', args_delim),
  94. 'DEPENDS': extract_macro_calls(unit, 'TEST_DEPENDS_VALUE', args_delim),
  95. 'IDEA_EXCLUDE': extract_macro_calls(unit, 'IDEA_EXCLUDE_DIRS_VALUE', args_delim),
  96. 'IDEA_RESOURCE': extract_macro_calls(unit, 'IDEA_RESOURCE_DIRS_VALUE', args_delim),
  97. 'IDEA_MODULE_NAME': extract_macro_calls(unit, 'IDEA_MODULE_NAME_VALUE', args_delim),
  98. 'GENERATE_SCRIPT': extract_macro_calls2(unit, 'GENERATE_SCRIPT_VALUE'),
  99. 'FAKEID': extract_macro_calls(unit, 'FAKEID', args_delim),
  100. 'TEST_DATA': extract_macro_calls(unit, 'TEST_DATA_VALUE', args_delim),
  101. 'JAVA_FORBIDDEN_LIBRARIES': extract_macro_calls(unit, 'JAVA_FORBIDDEN_LIBRARIES_VALUE', args_delim),
  102. 'JDK_RESOURCE': 'JDK' + (unit.get('JDK_VERSION') or unit.get('JDK_REAL_VERSION') or '_DEFAULT')
  103. }
  104. if unit.get('ENABLE_PREVIEW_VALUE') == 'yes' and (unit.get('JDK_VERSION') or unit.get('JDK_REAL_VERSION')) in ('15', '16', '17'):
  105. data['ENABLE_PREVIEW'] = extract_macro_calls(unit, 'ENABLE_PREVIEW_VALUE', args_delim)
  106. if unit.get('SAVE_JAVAC_GENERATED_SRCS_DIR') and unit.get('SAVE_JAVAC_GENERATED_SRCS_TAR'):
  107. data['SAVE_JAVAC_GENERATED_SRCS_DIR'] = extract_macro_calls(unit, 'SAVE_JAVAC_GENERATED_SRCS_DIR', args_delim)
  108. data['SAVE_JAVAC_GENERATED_SRCS_TAR'] = extract_macro_calls(unit, 'SAVE_JAVAC_GENERATED_SRCS_TAR', args_delim)
  109. if unit.get('JAVA_ADD_DLLS_VALUE') == 'yes':
  110. data['ADD_DLLS_FROM_DEPENDS'] = extract_macro_calls(unit, 'JAVA_ADD_DLLS_VALUE', args_delim)
  111. if unit.get('ERROR_PRONE_VALUE') == 'yes':
  112. data['ERROR_PRONE'] = extract_macro_calls(unit, 'ERROR_PRONE_VALUE', args_delim)
  113. if unit.get('WITH_KOTLIN_VALUE') == 'yes':
  114. data['WITH_KOTLIN'] = extract_macro_calls(unit, 'WITH_KOTLIN_VALUE', args_delim)
  115. if unit.get('KOTLIN_JVM_TARGET'):
  116. data['KOTLIN_JVM_TARGET'] = extract_macro_calls(unit, 'KOTLIN_JVM_TARGET', args_delim)
  117. if unit.get('KOTLINC_FLAGS_VALUE'):
  118. data['KOTLINC_FLAGS'] = extract_macro_calls(unit, 'KOTLINC_FLAGS_VALUE', args_delim)
  119. if unit.get('KOTLINC_OPTS_VALUE'):
  120. data['KOTLINC_OPTS'] = extract_macro_calls(unit, 'KOTLINC_OPTS_VALUE', args_delim)
  121. if unit.get('DIRECT_DEPS_ONLY_VALUE') == 'yes':
  122. data['DIRECT_DEPS_ONLY'] = extract_macro_calls(unit, 'DIRECT_DEPS_ONLY_VALUE', args_delim)
  123. if unit.get('JAVA_EXTERNAL_DEPENDENCIES_VALUE'):
  124. valid = []
  125. for dep in sum(extract_macro_calls(unit, 'JAVA_EXTERNAL_DEPENDENCIES_VALUE', args_delim), []):
  126. if os.path.normpath(dep).startswith('..'):
  127. ymake.report_configure_error('{}: {} - relative paths in JAVA_EXTERNAL_DEPENDENCIES is not allowed'.format(unit.path(), dep))
  128. elif os.path.isabs(dep):
  129. ymake.report_configure_error('{}: {} absolute paths in JAVA_EXTERNAL_DEPENDENCIES is not allowed'.format(unit.path(), dep))
  130. else:
  131. valid.append(dep)
  132. if valid:
  133. data['EXTERNAL_DEPENDENCIES'] = [valid]
  134. if unit.get('MAKE_UBERJAR_VALUE') == 'yes':
  135. if unit.get('MODULE_TYPE') != 'JAVA_PROGRAM':
  136. ymake.report_configure_error('{}: UBERJAR supported only for JAVA_PROGRAM module type'.format(unit.path()))
  137. data['UBERJAR'] = extract_macro_calls(unit, 'MAKE_UBERJAR_VALUE', args_delim)
  138. data['UBERJAR_PREFIX'] = extract_macro_calls(unit, 'UBERJAR_PREFIX_VALUE', args_delim)
  139. data['UBERJAR_HIDE_EXCLUDE'] = extract_macro_calls(unit, 'UBERJAR_HIDE_EXCLUDE_VALUE', args_delim)
  140. data['UBERJAR_PATH_EXCLUDE'] = extract_macro_calls(unit, 'UBERJAR_PATH_EXCLUDE_VALUE', args_delim)
  141. data['UBERJAR_MANIFEST_TRANSFORMER_MAIN'] = extract_macro_calls(unit, 'UBERJAR_MANIFEST_TRANSFORMER_MAIN_VALUE', args_delim)
  142. data['UBERJAR_MANIFEST_TRANSFORMER_ATTRIBUTE'] = extract_macro_calls(unit, 'UBERJAR_MANIFEST_TRANSFORMER_ATTRIBUTE_VALUE', args_delim)
  143. data['UBERJAR_APPENDING_TRANSFORMER'] = extract_macro_calls(unit, 'UBERJAR_APPENDING_TRANSFORMER_VALUE', args_delim)
  144. data['UBERJAR_SERVICES_RESOURCE_TRANSFORMER'] = extract_macro_calls(unit, 'UBERJAR_SERVICES_RESOURCE_TRANSFORMER_VALUE', args_delim)
  145. if unit.get('WITH_JDK_VALUE') == 'yes':
  146. if unit.get('MODULE_TYPE') != 'JAVA_PROGRAM':
  147. ymake.report_configure_error('{}: JDK export supported only for JAVA_PROGRAM module type'.format(unit.path()))
  148. data['WITH_JDK'] = extract_macro_calls(unit, 'WITH_JDK_VALUE', args_delim)
  149. if not data['EXTERNAL_JAR']:
  150. has_processor = extract_macro_calls(unit, 'GENERATE_VCS_JAVA_INFO_NODEP', args_delim)
  151. data['EMBED_VCS'] = [[str(has_processor and has_processor[0] and has_processor[0][0])]]
  152. # FORCE_VCS_INFO_UPDATE is responsible for setting special value of VCS_INFO_DISABLE_CACHE__NO_UID__
  153. macro_val = extract_macro_calls(unit, 'FORCE_VCS_INFO_UPDATE', args_delim)
  154. macro_str = macro_val[0][0] if macro_val and macro_val[0] and macro_val[0][0] else ''
  155. if macro_str and macro_str == 'yes':
  156. data['VCS_INFO_DISABLE_CACHE__NO_UID__'] = macro_val
  157. for java_srcs_args in data['JAVA_SRCS']:
  158. external = None
  159. for i in xrange(len(java_srcs_args)):
  160. arg = java_srcs_args[i]
  161. if arg == 'EXTERNAL':
  162. if not i + 1 < len(java_srcs_args):
  163. continue # TODO configure error
  164. ex = java_srcs_args[i + 1]
  165. if ex in ('EXTERNAL', 'SRCDIR', 'PACKAGE_PREFIX', 'EXCLUDE'):
  166. continue # TODO configure error
  167. if external is not None:
  168. continue # TODO configure error
  169. external = ex
  170. if external:
  171. unit.onpeerdir(external)
  172. for k, v in data.items():
  173. if not v:
  174. data.pop(k)
  175. dart = 'JAVA_DART: ' + base64.b64encode(json.dumps(data)) + '\n' + DELIM + '\n'
  176. unit.set_property(['JAVA_DART_DATA', dart])
  177. if not idea_only and unit.get('MODULE_TYPE') in ('JAVA_PROGRAM', 'JAVA_LIBRARY', 'JTEST', 'TESTNG', 'JUNIT5') and not unit.path().startswith('$S/contrib/java'):
  178. unit.on_add_classpath_clash_check()
  179. if unit.get('LINT_LEVEL_VALUE') != "none":
  180. unit.onadd_check(['JAVA_STYLE', unit.get('LINT_LEVEL_VALUE')])
  181. def on_add_java_style_checks(unit, *args):
  182. if unit.get('LINT_LEVEL_VALUE') != "none":
  183. unit.onadd_check(['JAVA_STYLE', unit.get('LINT_LEVEL_VALUE')] + list(args))
  184. def on_add_classpath_clash_check(unit, *args):
  185. jdeps_val = (unit.get('CHECK_JAVA_DEPS_VALUE') or '').lower()
  186. if jdeps_val and jdeps_val not in ('yes', 'no', 'strict'):
  187. ymake.report_configure_error('CHECK_JAVA_DEPS: "yes", "no" or "strict" required')
  188. if jdeps_val and jdeps_val != 'no':
  189. unit.onjava_test_deps(jdeps_val)
  190. # Ymake java modules related macroses
  191. def onexternal_jar(unit, *args):
  192. args = list(args)
  193. flat, kv = common.sort_by_keywords({'SOURCES': 1}, args)
  194. if not flat:
  195. ymake.report_configure_error('EXTERNAL_JAR requires exactly one resource URL of compiled jar library')
  196. res = flat[0]
  197. resid = res[4:] if res.startswith('sbr:') else res
  198. unit.set(['JAR_LIB_RESOURCE', resid])
  199. unit.set(['JAR_LIB_RESOURCE_URL', res])
  200. def on_check_java_srcdir(unit, *args):
  201. args = list(args)
  202. for arg in args:
  203. if not '$' in arg:
  204. arc_srcdir = os.path.join(unit.get('MODDIR'), arg)
  205. abs_srcdir = unit.resolve(os.path.join("$S/", arc_srcdir))
  206. if not os.path.exists(abs_srcdir) or not os.path.isdir(abs_srcdir):
  207. ymake.report_configure_error(
  208. 'Trying to set a [[alt1]]JAVA_SRCS[[rst]] for a missing directory: [[imp]]$S/{}[[rst]]',
  209. missing_dir=arc_srcdir
  210. )
  211. return
  212. srcdir = unit.resolve_arc_path(arg)
  213. if srcdir and not srcdir.startswith('$S'):
  214. continue
  215. abs_srcdir = unit.resolve(srcdir) if srcdir else unit.resolve(arg)
  216. if not os.path.exists(abs_srcdir) or not os.path.isdir(abs_srcdir):
  217. ymake.report_configure_error(
  218. 'Trying to set a [[alt1]]JAVA_SRCS[[rst]] for a missing directory: [[imp]]{}[[rst]]',
  219. missing_dir=srcdir
  220. )
  221. def on_fill_jar_copy_resources_cmd(unit, *args):
  222. if len(args) == 4:
  223. varname, srcdir, base_classes_dir, reslist = tuple(args)
  224. package = ''
  225. else:
  226. varname, srcdir, base_classes_dir, package, reslist = tuple(args)
  227. dest_dir = os.path.join(base_classes_dir, *package.split('.')) if package else base_classes_dir
  228. var = unit.get(varname)
  229. var += ' && $FS_TOOLS copy_files {} {} {}'.format(srcdir if srcdir.startswith('"$') else '${CURDIR}/' + srcdir, dest_dir, reslist)
  230. unit.set([varname, var])
  231. def on_fill_jar_gen_srcs(unit, *args):
  232. varname, jar_type, srcdir, base_classes_dir, java_list, kt_list, groovy_list, res_list = tuple(args[0:8])
  233. resolved_srcdir = unit.resolve_arc_path(srcdir)
  234. if not resolved_srcdir.startswith('$') or resolved_srcdir.startswith('$S'):
  235. return
  236. exclude_pos = args.index('EXCLUDE')
  237. globs = args[7:exclude_pos]
  238. excludes = args[exclude_pos + 1:]
  239. var = unit.get(varname)
  240. var += ' && ${{cwd:BINDIR}} $YMAKE_PYTHON ${{input:"build/scripts/resolve_java_srcs.py"}} --append -d {} -s {} -k {} -g {} -r {} --include-patterns {}'.format(srcdir, java_list, kt_list, groovy_list, res_list, ' '.join(globs))
  241. if jar_type == 'SRC_JAR':
  242. var += ' --all-resources'
  243. if len(excludes) > 0:
  244. var += ' --exclude-patterns {}'.format(' '.join(excludes))
  245. if unit.get('WITH_KOTLIN_VALUE') == 'yes':
  246. var += ' --resolve-kotlin'
  247. unit.set([varname, var])
  248. def on_check_run_java_prog_classpath(unit, *args):
  249. if len(args) != 1:
  250. ymake.report_configure_error('multiple CLASSPATH elements in RUN_JAVA_PROGRAM invocation no more supported. Use JAVA_RUNTIME_PEERDIR on the JAVA_PROGRAM module instead')
  251. def extract_words(words, keys):
  252. kv = {}
  253. k = None
  254. for w in words:
  255. if w in keys:
  256. k = w
  257. else:
  258. if not k in kv:
  259. kv[k] = []
  260. kv[k].append(w)
  261. return kv
  262. def parse_words(words):
  263. kv = extract_words(words, {'OUT', 'TEMPLATE'})
  264. if not 'TEMPLATE' in kv:
  265. kv['TEMPLATE'] = ['template.tmpl']
  266. ws = []
  267. for item in ('OUT', 'TEMPLATE'):
  268. for i, word in list(enumerate(kv[item])):
  269. if word == 'CUSTOM_PROPERTY':
  270. ws += kv[item][i:]
  271. kv[item] = kv[item][:i]
  272. templates = kv['TEMPLATE']
  273. outputs = kv['OUT']
  274. if len(outputs) < len(templates):
  275. ymake.report_configure_error('To many arguments for TEMPLATE parameter')
  276. return
  277. if ws and ws[0] != 'CUSTOM_PROPERTY':
  278. ymake.report_configure_error('''Can't parse {}'''.format(ws))
  279. custom_props = []
  280. for item in ws:
  281. if item == 'CUSTOM_PROPERTY':
  282. custom_props.append([])
  283. else:
  284. custom_props[-1].append(item)
  285. props = []
  286. for p in custom_props:
  287. if not p:
  288. ymake.report_configure_error('Empty CUSTOM_PROPERTY')
  289. continue
  290. props.append('-B')
  291. if len(p) > 1:
  292. props.append(base64.b64encode("{}={}".format(p[0], ' '.join(p[1:]))))
  293. else:
  294. ymake.report_configure_error('CUSTOM_PROPERTY "{}" value is not specified'.format(p[0]))
  295. for i, o in enumerate(outputs):
  296. yield o, templates[min(i, len(templates) - 1)], props
  297. def on_ymake_generate_script(unit, *args):
  298. for out, tmpl, props in parse_words(list(args)):
  299. unit.on_add_gen_java_script([out, tmpl] + list(props))
  300. def on_jdk_version_macro_check(unit, *args):
  301. if len(args) != 1:
  302. unit.message(["error", "Invalid syntax. Single argument required."])
  303. jdk_version = args[0]
  304. available_versions = ('10', '11', '12', '13', '14', '15', '16', '17',)
  305. if jdk_version not in available_versions:
  306. unit.message(["error", "Invalid jdk version: {}. {} are available".format(jdk_version, available_versions)])