diff options
author | trivias <trivias@yandex-team.com> | 2023-03-20 18:05:59 +0300 |
---|---|---|
committer | trivias <trivias@yandex-team.com> | 2023-03-20 18:05:59 +0300 |
commit | 30574d399b041f91ad5143e57acca97636a54085 (patch) | |
tree | dd7696fff1e47c9d5cc243f4b0c1899ea6df925d | |
parent | e0305489c73afe7196823399dc37f5d8031c0ca2 (diff) | |
download | ydb-30574d399b041f91ad5143e57acca97636a54085.tar.gz |
Idl plugin for ymake
-rw-r--r-- | build/conf/project_specific/maps/mapkit.conf | 55 | ||||
-rw-r--r-- | build/plugins/maps_mobile_idl.py | 1160 | ||||
-rw-r--r-- | build/plugins/ya.make | 1 | ||||
-rw-r--r-- | build/scripts/link_jsrc.py | 27 |
4 files changed, 1240 insertions, 3 deletions
diff --git a/build/conf/project_specific/maps/mapkit.conf b/build/conf/project_specific/maps/mapkit.conf index 67ffa3bf26..c5d2882937 100644 --- a/build/conf/project_specific/maps/mapkit.conf +++ b/build/conf/project_specific/maps/mapkit.conf @@ -15,8 +15,7 @@ macro MAPS_IDL_ADDINCL(Dirs...) { ### ### Proxy macro for MAPKITIDL which adds PEERDIR to YMAKE resources macro _MAPKITIDL_PROXY(Args...) { - MAPKITIDL($Args) - ENABLE(USE_YMAKE_RESOURCE) + _PROCESS_MAPS_MOBILE_IDL($Args) SET_APPEND(_MAKEFILE_INCLUDE_LIKE_TEXT_DEPS ${ext=idl:Args}) } @@ -86,6 +85,26 @@ macro USE_IDL_CPP() { SET(PEERDIR_TAGS __EMPTY__ CPP_PROTO H_CPP_IDL) } + +_LINK_JSRC=${cwd:ARCADIA_BUILD_ROOT} $YMAKE_PYTHON3 ${input:"build/scripts/link_jsrc.py"} --input $AUTO_INPUT --output $TARGET ${kv;hide:"p JL"} ${kv;hide:"pc light-blue"} ${kv;hide:"show_out"} +_LINK_JSRC_MF=$_LINK_JSRC && $GENERATE_MF + +### @usage: MAPS_IDL_JSRC_LIBRARY +### +### Create .jsrc output - archive of megred .jsrc inputs +module MAPS_IDL_JSRC_LIBRARY: JSRC_LIBRARY { + .CMD=_LINK_JSRC_MF + .EXTS=.jsrc +} + +### _MAPS_IDL_LIBRARY_EPILOGUE() #internal +### +### This macro executes macros which should be invoked after all user specified macros in the ya.make file +macro _MAPS_IDL_LIBRARY_EPILOGUE() { + _GLOB(MAPKIT_IDL_FRAMEWORK_FILES ${ARCADIA_ROOT}/${MAPKIT_IDL_FRAMEWORK}/*.framework) + SET_APPEND(_MAKEFILE_INCLUDE_LIKE_DEPS $MAPKIT_IDL_FRAMEWORK_FILES) +} + ### @usage: MAPS_IDL_LIBRARY() ### ### Definition of multimodule that builds various variants of libraries. @@ -95,6 +114,7 @@ macro USE_IDL_CPP() { multimodule MAPS_IDL_LIBRARY { module H_IDL: LIBRARY { .ALIASES=SRCS=MAPS_IDL_SRCS GLOBAL_SRCS=MAPS_IDL_GLOBAL_SRCS ADDINCL=MAPKIT_ADDINCL MAPSMOBI_SRCS=MAPS_IDL_SRCS + .EPILOGUE=_MAPS_IDL_LIBRARY_EPILOGUE ENABLE(H_IDL) ENABLE(CPP_IDL) @@ -110,6 +130,7 @@ multimodule MAPS_IDL_LIBRARY { .ALIASES=SRCS=MAPS_IDL_SRCS GLOBAL_SRCS=MAPS_IDL_GLOBAL_SRCS ADDINCL=MAPKIT_ADDINCL MAPSMOBI_SRCS=MAPS_IDL_SRCS .DEFAULT_NAME_GENERATOR=DirName .PEERDIRSELF=H_IDL + .EPILOGUE=_MAPS_IDL_LIBRARY_EPILOGUE ENABLE(H_CPP_IDL) ENABLE(CPP_IDL) @@ -120,9 +141,10 @@ multimodule MAPS_IDL_LIBRARY { NO_UTIL() } - module JAVA_IDL: JSRC_LIBRARY { + module JAVA_IDL: MAPS_IDL_JSRC_LIBRARY { .ALIASES=SRCS=MAPS_IDL_SRCS GLOBAL_SRCS=MAPS_IDL_SRCS ADDINCL=MAPKIT_ADDINCL MAPSMOBI_SRCS=MAPS_IDL_SRCS .IGNORED=CPP_ADDINCL + .EPILOGUE=_MAPS_IDL_LIBRARY_EPILOGUE SET(PEERDIR_TAGS JAVA_IDL JAVA_PROTO) SET(MAPS_IDL_FILTER FILTER .java) ENABLE(JAVA_IDL) @@ -131,6 +153,33 @@ multimodule MAPS_IDL_LIBRARY { } } +_IDL_TOOL_EXTRA_ARGS= +when ($MAPS_MOBILE_USE_STD_VARIANT == "yes") { + _IDL_TOOL_EXTRA_ARGS+=--use-std-variant +} +when ($MAPS_MOBILE_PUBLIC_API == "yes") { + _IDL_TOOL_EXTRA_ARGS+=--public +} +when ($MAPS_MOBILE_EXPLICIT_SERIALIZATION_ONLY == "yes") { + _IDL_TOOL_EXTRA_ARGS+=--explicit-serialization-only +} + +_IDL_TOOL_CMDLINE=$GENERATE_MF && ${cwd:ARCADIA_ROOT} ${tool:"tools/idl/bin"} --in-proto-root maps/doc/proto --base-proto-package yandex.maps.proto ${pre=--out-base-root :OUT_BASE_ROOT} ${pre=--out-android-root :OUT_ANDROID_ROOT} ${pre=--out-ios-root :OUT_IOS_ROOT} ${pre=-F :FRAMEWORK_DIRS} ${pre=-I :INCLUDES} ${_IDL_TOOL_EXTRA_ARGS} ${hide;input:IN} ${hide;output:OUT} ${hide;output;noauto:OUT_NOAUTO} ${hide;output_include:OUTPUT_INCLUDES} ${IDLS} ${kv;hide:"p ID"} ${kv;hide:"pc white"} ${hide:MAPSMOBI_FAKEID} ${hide;suf=filter_in;join=|:FILTER_IN} ${hide;suf=filter_out;join=|:FILTER_OUT} ${kv;hide:"show_out"} + +### @usage: _RUN_IDL_TOOL(...) # internal +### +### Run idl tool within non-JAVA_IDL submodule. This macro is called from _PROCESS_MAPS_MOBILE_IDL macro. +macro _RUN_IDL_TOOL(OUT_BASE_ROOT="", OUT_ANDROID_ROOT="", OUT_IOS_ROOT="", IN[], OUT[], OUT_NOAUTO[], OUTPUT_INCLUDES[], INCLUDES[], FRAMEWORK_DIRS[], IDLS[], FILTER_IN[], FILTER_OUT[]) { + .CMD=$_IDL_TOOL_CMDLINE +} + +### @usage: _RUN_IDL_TOOL(...) # internal +### +### Run idl tool within JAVA_IDL submodule. This macro is called from _PROCESS_MAPS_MOBILE_IDL macro. +macro _RUN_IDL_TOOL_JAVA(OUT_BASE_ROOT="", OUT_ANDROID_ROOT="", OUT_IOS_ROOT="", IN[], OUT[], OUT_NOAUTO[], OUTPUT_INCLUDES[], INCLUDES[], FRAMEWORK_DIRS[], IDLS[], FILTER_IN[], FILTER_OUT[]) { + .CMD=$_IDL_TOOL_CMDLINE && $YMAKE_PYTHON3 ${input:"build/scripts/tar_sources.py"} --output ${output;suf=$_HASH_HELPER($IDLS).jsrc:"_"} --input $OUT_ANDROID_ROOT --exts .java +} + ### @usage: MAPSMOBI_COLLECT_ASSETS_FILES(varname [dir]) # internal ### ### This macro is strictly prohibited to use outside of mapsmobi project diff --git a/build/plugins/maps_mobile_idl.py b/build/plugins/maps_mobile_idl.py new file mode 100644 index 0000000000..8f5a1efcfb --- /dev/null +++ b/build/plugins/maps_mobile_idl.py @@ -0,0 +1,1160 @@ +import os +import re +from collections import namedtuple + +from _common import sort_by_keywords + +Framework = namedtuple('Framework', [ + 'cpp_namespace_tokens', + 'java_class_path', + 'objc_framework_name', + 'objc_framework_prefix' + ]) + + +def _get_proto_header_file(proto_file_name): + return proto_file_name.split('.')[0] + '.pb.h' + + +def _get_appended_values(unit, key): + value = [] + raw_value = unit.get(key) + if raw_value: + value = filter(lambda x: len(x) > 0, raw_value.split(' ')) + assert len(value) == 0 or value[0] == '$' + key + return value[1:] if len(value) > 0 else value + + +def _load_framework_file_list(unit): + frameworks = [unit.resolve(unit.resolve_arc_path(os.sep.join(path.split(os.sep)[1:]))) for path in unit.get('MAPKIT_IDL_FRAMEWORK_FILES').split(' ')] + return frameworks + + +def _require_framework_entry(entry, framework): + if entry not in framework: + raise Exception('No {} entry in {} framework'.format(entry, framework)) + + +def _read_framework(unit, framework_file): + file_path = unit.resolve(framework_file) + result = {} + with open(file_path, 'r') as f: + lineId = 0 + for line in f: + lineId += 1 + tokens = line.split('=') + if len(tokens) != 2: + raise Exception('Malformed idl framework file {} line {}'.format(framework_file, lineId)) + result[tokens[0].strip()] = tokens[1].strip() + + _require_framework_entry('CPP_NAMESPACE', result) + _require_framework_entry('JAVA_PACKAGE', result) + _require_framework_entry('OBJC_FRAMEWORK', result) + _require_framework_entry('OBJC_FRAMEWORK_PREFIX', result) + return Framework( + result['CPP_NAMESPACE'].split('.'), + result['JAVA_PACKAGE'], + result['OBJC_FRAMEWORK'], + result['OBJC_FRAMEWORK_PREFIX']) + + +def _read_frameworks(unit): + framework_file_list = _load_framework_file_list(unit) + result = {} + for file_name in framework_file_list: + name = file_name.split(os.sep)[-1].split('.')[0] + result[name] = _read_framework(unit, file_name) + return result + + +def _extract_by_regexp(line, regexp): + re_match = regexp.search(line) + if not re_match: + return None + return re_match.group(1) + + +class RegExp: + OBJC_INFIX = r'\bobjc_infix\s*([^\s]+);' + + IMPORT = r'^import\s+"([^"]+)"' + + WEAK_INTERFACE = r'\bweak_ref\s+interface\b' + SHARED_INTERFACE = r'\bshared_ref\s+interface\b' + STRONG_INTERFACE = r'^\s*interface\b' + NATIVE_LISTENER = r'\bnative\s+listener\b' + STATIC_INTERFACE = r'\bstatic\s+interface\b' + VIEW_DELEGATE = r'\bview_delegate\b' + + CUSTOM_PROTO_HEADER = r'^\s*protoconv\s+"([^"]+)"\s*$' + BASED_ON_PROTO_START = r'\bbased\s+on(\s|$)' + BASED_ON_PROTO = r'\bbased\s+on\s+"([^"]+)"\s*:' + + CUSTOM_CPP_HEADER = r'^\s*cpp\s+"([^"]+)"\s*$' + STRUCT = r'\bstruct\b' + + LITE_STRUCT = r'\blite\s+struct\b' + BRIDGED_STRUCT = r'^(\s*options|\s*serializable|\s*abstract)*\s*struct\s+' + + LAMBDA_LISTENER = r'\blambda\s+listener\b' + LISTENER = r'^\s*listener\s+' + PLATFORM_INTERFACE = r'platform\s+interface' + + VARIANT = r'\bvariant\b' + + OPTIONAL = r'\boptional\b' + INT_64 = r'\bint64\b' + STRING = r'\bstring\b' + POINT = r'\bpoint\b' + BYTES = r'\bbytes\b' + VECTOR = r'\bvector\b' + DICTIONARY = r'\bdictionary\b' + ANY = r'\bany[^_]' + ANY_COLLECTION = r'\bany_collection\b' + ENUM = r'\benum\b' + TIME = r'\b(time_interval|abs_timestamp|rel_timestamp)\b' + BITMAP = r'\bbitmap\b' + VIEW_PROVIDER = r'\bview_provider\b' + IMAGE_PROVIDER = r'\bimage_provider\b' + ANIMATED_IMAGE_PROVIDER = r'\banimated_image_provider\b' + MODEL_PROVIDER = r'\bmodel_provider\b' + ANIMATED_MODEL_PROVIDER = r'\banimated_model_provider\b' + COLOR = r'\bcolor\b' + PLATFORM_VIEW = r'\bplatform_view\b' + ERROR = r'\b(runtime\.)?Error\b' + TYPE_DICTIONARY = r'\btype_dictionary\b' + + SERIALIZABLE = r'\bserializable\b' + + +class OutputType: + BASE_HEADER = 1 + STRUCT_SOURCE = 2 + PROTOCONV_HEADER = 3 + PROTOCONV_SOURCE = 4 + ANDROID_HEADER = 5 + ANDROID_SOURCE = 6 + IOS_HEADER = 7 + IOS_SOURCE = 8 + IOS_PRIVATE_HEADER = 9 + IOS_BINDING_SOURCE = 10 + + +class OutputNameGenerator: + def __init__(self, file_path, frameworks): + path_tokens = file_path.split(os.sep) + framework_name = path_tokens[0] + self._framework = frameworks[framework_name] + self._cpp_namespace_tokens = self._framework.cpp_namespace_tokens + path_tokens[1:-1] + file_name = path_tokens[-1] + self._cpp_name = file_name.split('.')[0] + + name_tokens = self._cpp_name.split('_') + self._objc_name_core = ''.join((self._capitalize(token) for token in name_tokens)) + self._objc_name = self._framework.objc_framework_prefix + self._objc_name_core + + def set_objc_infix(self, objc_infix): + self._objc_name = self._framework.objc_framework_prefix + objc_infix + self._objc_name_core + + def is_header(self, output_type): + return output_type in [ + OutputType.BASE_HEADER, + OutputType.PROTOCONV_HEADER, + OutputType.ANDROID_HEADER, + OutputType.IOS_HEADER, + OutputType.IOS_PRIVATE_HEADER] + + def _cpp_file_name(self, extension, additional_tokens=[]): + path_tokens = self._cpp_namespace_tokens + additional_tokens + [self._cpp_name + extension] + return os.path.join(*path_tokens) + + def _objc_file_name(self, extension, additional_tokens=[]): + path_tokens = [self._framework.objc_framework_name] + additional_tokens + [self._objc_name + extension] + return os.path.join(*path_tokens) + + def _capitalize(self, word): + return word[:1].upper() + word[1:] + + def generate_name(self, output_type): + if output_type is OutputType.BASE_HEADER: + return self._cpp_file_name('.h') + + if output_type is OutputType.STRUCT_SOURCE: + return self._cpp_file_name('.cpp') + + if output_type is OutputType.PROTOCONV_HEADER: + return self._cpp_file_name('.conv.h') + + if output_type is OutputType.PROTOCONV_SOURCE: + return self._cpp_file_name('.conv.cpp') + + if output_type is OutputType.ANDROID_HEADER: + return self._cpp_file_name('_binding.h', ['internal', 'android']) + + if output_type is OutputType.ANDROID_SOURCE: + return self._cpp_file_name('_binding.cpp', ['internal', 'android']) + + if output_type is OutputType.IOS_HEADER: + return self._objc_file_name('.h') + + if output_type is OutputType.IOS_SOURCE: + return self._objc_file_name('.m') + + if output_type is OutputType.IOS_PRIVATE_HEADER: + return self._objc_file_name('_Private.h', ['Internal']) + + if output_type is OutputType.IOS_BINDING_SOURCE: + return self._objc_file_name('_Binding.mm') + + def generate_path(self, output_type): + name = self.generate_name(output_type) + + if self.is_header(output_type): + return os.path.join('include', name) + + return os.path.join('impl', name) + + +class ProcessContext: + def __init__(self, unit, frameworks, file_paths): + self.unit = unit + self.frameworks = frameworks + self.file_paths = file_paths + self.is_ios = unit.enabled("OS_IOS") + self.is_android = unit.enabled("OS_ANDROID") + self.output_name_generator = None + self.add_generated_output_includes = unit.enabled("H_CPP_IDL") + + def runtime_include(self, include_name): + name_tokens = self.frameworks['runtime'].cpp_namespace_tokens + [include_name] + return os.path.join(*name_tokens) + + def runtime_objc_import(self, import_name): + return os.path.join( + self.frameworks['runtime'].objc_framework_name, + self.frameworks['runtime'].objc_framework_prefix + import_name + ) + + +class BaseRule: + def __init__(self, context): + self.context = context + + def start_file(self, file_path): + pass + + def process_line(self, line): + pass + + def get_output_types(self): + return set() + + def get_output_includes(self): + return set() + + +class ObjcInfixRule(BaseRule): + def __init__(self, context): + BaseRule.__init__(self, context) + self._found_infix = False + self._reg_exp = re.compile(RegExp.OBJC_INFIX) + + def start_file(self, file_path): + BaseRule.start_file(self, file_path) + self.context.output_name_generator.set_objc_infix('') + self._found_infix = False + + def process_line(self, line): + BaseRule.process_line(self, line) + if self._found_infix: + return + + infix = _extract_by_regexp(line, self._reg_exp) + if infix: + self._found_infix = True + self.context.output_name_generator.set_objc_infix(infix) + + +class ImportRule(BaseRule): + def __init__(self, context): + BaseRule.__init__(self, context) + self._imports = set() + self._import_reg_exp = re.compile(RegExp.IMPORT) + + def start_file(self, file_path): + self._imports = set() + + def process_line(self, line): + BaseRule.process_line(self, line) + idl_import = _extract_by_regexp(line, self._import_reg_exp) + if idl_import: + self._imports.add(idl_import) + + def get_output_includes(self): + result = set() + for idl_import in self._imports: + if idl_import in self.context.file_paths: + continue + + name_generator = OutputNameGenerator(idl_import, self.context.frameworks) + result.add(name_generator.generate_name(OutputType.BASE_HEADER)) + + return result + + +class DefaultRule(BaseRule): + def __init__(self, context): + BaseRule.__init__(self, context) + + def get_output_types(self): + result = set() + result.add(OutputType.BASE_HEADER) + if self.context.is_ios: + result.add(OutputType.IOS_HEADER) + result.add(OutputType.IOS_SOURCE) + + return result + + def get_output_includes(self): + result = set() + result.add('yandex/maps/export.h') + result.add(self.context.runtime_include('assert.h')) + result.add(self.context.runtime_include('exception.h')) + result.add(self.context.runtime_include('bindings/traits.h')) + + if self.context.is_ios: + result.add(self.context.runtime_include('bindings/platform.h')) + result.add('Foundation/Foundation.h') + result.add(self.context.runtime_include('ios/object.h')) + result.add(self.context.runtime_include('bindings/ios/to_native.h')) + result.add(self.context.runtime_include('bindings/ios/to_platform.h')) + result.add(self.context.runtime_include('ios/exception.h')) + result.add(self.context.runtime_objc_import('Subscription.h')) + result.add(self.context.runtime_objc_import('Export.h')) + + if self.context.is_android: + result.add(self.context.runtime_include('bindings/platform.h')) + result.add(self.context.runtime_include('android/object.h')) + result.add(self.context.runtime_include('bindings/android/to_native.h')) + result.add(self.context.runtime_include('bindings/android/to_platform.h')) + result.add(self.context.runtime_include('exception.h')) + + return result + + +class CheckRule(BaseRule): + def __init__( + self, + context, + output_types=set(), + output_includes=set(), + ios_output_types=set(), + ios_output_includes=set(), + android_output_types=set(), + android_output_includes=set() + ): + BaseRule.__init__(self, context) + self._output_types = output_types + self._output_includes = output_includes + self._ios_output_types = ios_output_types + self._ios_output_includes = ios_output_includes + self._android_output_types = android_output_types + self._android_output_includes = android_output_includes + + def triggered_on_file(self): + pass + + def get_output_types(self): + result = set() + if self.triggered_on_file(): + result.update(self._output_types) + + if self.context.is_ios: + result.update(self._ios_output_types) + + if self.context.is_android: + result.update(self._android_output_types) + + return result + + def get_output_includes(self): + result = set() + if self.triggered_on_file(): + result.update(self._output_includes) + + if self.context.is_ios: + result.update(self._ios_output_includes) + + if self.context.is_android: + result.update(self._android_output_includes) + + return result + + +class OrRule(CheckRule): + def __init__(self, check_rules, *args, **kwargs): + CheckRule.__init__(self, *args, **kwargs) + self._rules = check_rules + + def triggered_on_file(self): + return any((rule.triggered_on_file() for rule in self._rules)) + + +class AndRule(CheckRule): + def __init__(self, check_rules, *args, **kwargs): + CheckRule.__init__(self, *args, **kwargs) + self._rules = check_rules + + def triggered_on_file(self): + return all((rule.triggered_on_file() for rule in self._rules)) + + +class RegExpRule(CheckRule): + def __init__(self, reg_exp_string, *args, **kwargs): + CheckRule.__init__(self, *args, **kwargs) + self._reg_exp = re.compile(reg_exp_string) + self._reg_exp_found_file = False + + def start_file(self, file_path): + CheckRule.start_file(self, file_path) + self._reg_exp_found_file = False + + def process_line(self, line): + CheckRule.process_line(self, line) + if self._reg_exp_found_file: + return + + if self._reg_exp.search(line) is not None: + self._reg_exp_found_file = True + + def triggered_on_file(self): + return self._reg_exp_found_file + + +class ProtoRule(BaseRule): + def __init__(self, context): + BaseRule.__init__(self, context) + self._file_has_non_custom_proto = False + self._currently_custom_proto = False + self._currently_based_on = False + self._running_line = '' + self._custom_proto_headers = set() + self._proto_files = set() + + self._custom_proto_reg_exp = re.compile(RegExp.CUSTOM_PROTO_HEADER) + self._based_on_proto_start_reg_exp = re.compile(RegExp.BASED_ON_PROTO_START) + self._based_on_proto_reg_exp = re.compile(RegExp.BASED_ON_PROTO) + + def start_file(self, file_path): + BaseRule.start_file(self, file_path) + self._currently_custom_proto = False + self._file_has_non_custom_proto = False + self._currently_based_on = False + self._running_line = '' + + def process_line(self, line): + BaseRule.process_line(self, line) + proto_header = _extract_by_regexp(line, self._custom_proto_reg_exp) + if proto_header: + self._custom_proto_headers.add(proto_header) + self._currently_based_on = False + self._running_line = '' + + self._currently_custom_proto = True + return + + if self._based_on_proto_start_reg_exp.search(line) is not None: + self._currently_based_on = True + self._running_line = '' + + if self._currently_based_on: + self._running_line += '\n' + line + proto_file = _extract_by_regexp(self._running_line, self._based_on_proto_reg_exp) + if proto_file: + self._currently_based_on = False + self._running_line = '' + self._proto_files.add(proto_file) + + if self._currently_custom_proto: + self._currently_custom_proto = False + else: + self._file_has_non_custom_proto = True + + def get_output_types(self): + if self._file_has_non_custom_proto: + return {OutputType.PROTOCONV_HEADER, OutputType.PROTOCONV_SOURCE} + return set() + + def get_output_includes(self): + result = set() + result.update(self._custom_proto_headers) + result.update((proto_file.split('.')[0] + '.pb.h' for proto_file in self._proto_files)) + + if self._file_has_non_custom_proto: + result.update({'vector'}) + + return result + + +class StructImplementationRule(BaseRule): + def __init__(self, context): + BaseRule.__init__(self, context) + self._file_has_non_custom_struct = False + self._custom_cpp_headers = set() + self._currently_custom_struct = False + + self._custom_cpp_header_reg_exp = re.compile(RegExp.CUSTOM_CPP_HEADER) + self._struct_reg_exp = re.compile(RegExp.STRUCT) + + def start_file(self, file_path): + BaseRule.start_file(self, file_path) + self._currently_custom_struct = False + self._file_has_non_custom_struct = False + + def process_line(self, line): + BaseRule.process_line(self, line) + + cpp_header = _extract_by_regexp(line, self._custom_cpp_header_reg_exp) + if cpp_header: + self._custom_cpp_headers.add(cpp_header) + self._currently_custom_struct = True + return + + if not self._file_has_non_custom_struct: + if self._struct_reg_exp.search(line) is not None: + if self._currently_custom_struct: + self._currently_custom_struct = False + else: + self._file_has_non_custom_struct = True + + def get_output_types(self): + result = set() + if self._file_has_non_custom_struct: + result.add(OutputType.STRUCT_SOURCE) + if self.context.is_ios: + result.add(OutputType.IOS_BINDING_SOURCE) + + return result + + def get_output_includes(self): + return self._custom_cpp_headers + + +class IdlFileProcessor: + def __init__(self, unit, frameworks, file_paths): + self._context = ProcessContext(unit, frameworks, file_paths) + self._resolved_idl_dir = unit.resolve(unit.resolve_arc_path(unit.path())) + self._outputs = set() + self._output_includes = set() + + self._rules = set() + + self._rules.add(ObjcInfixRule(self._context)) + self._rules.add(DefaultRule(self._context)) + self._rules.add(ImportRule(self._context)) + self._rules.add(ProtoRule(self._context)) + self._rules.add(StructImplementationRule(self._context)) + + view_delegate_rule = self._create_reg_exp_rule( + RegExp.VIEW_DELEGATE, + output_includes={self._context.runtime_include('view/view_delegate.h')} + ) + + weak_interface_rule = self._create_or_rule( + rules={ + self._create_reg_exp_rule(RegExp.WEAK_INTERFACE), + view_delegate_rule, + }, + output_includes={ + 'boost/any.hpp', + 'memory', + self._context.runtime_include('platform_holder.h') + } + ) + + strong_interface_rule = self._create_or_rule( + rules={ + self._create_reg_exp_rule(RegExp.STRONG_INTERFACE), + self._create_reg_exp_rule(RegExp.NATIVE_LISTENER) + } + ) + + non_static_interface_rule = self._create_or_rule( + rules={ + self._create_reg_exp_rule(RegExp.SHARED_INTERFACE), + strong_interface_rule, + weak_interface_rule + }, + ios_output_types={OutputType.IOS_PRIVATE_HEADER} + ) + + # interface rule + self._create_or_rule( + rules={ + self._create_reg_exp_rule(RegExp.STATIC_INTERFACE), + non_static_interface_rule + }, + ios_output_types={OutputType.IOS_BINDING_SOURCE}, + android_output_types={OutputType.ANDROID_SOURCE}, + ios_output_includes={'memory'} + ) + + bridged_struct_rule = self._create_reg_exp_rule( + RegExp.BRIDGED_STRUCT, + output_includes={ + 'memory', + self._context.runtime_include('bindings/platform.h') + }, + android_output_includes={ + self._context.runtime_include('any/android/to_platform.h'), + self._context.runtime_include('bindings/android/internal/new_serialization.h') + } + ) + + struct_rule = self._create_or_rule( + rules={ + self._create_reg_exp_rule(RegExp.LITE_STRUCT), + bridged_struct_rule + }, + ios_output_types={OutputType.IOS_PRIVATE_HEADER}, + android_output_types={OutputType.ANDROID_HEADER, OutputType.ANDROID_SOURCE}, + ios_output_includes={ + self._context.runtime_include('any/ios/to_platform.h'), + self._context.runtime_objc_import('NativeObject.h') + } + ) + + lambda_listener_rule = self._create_reg_exp_rule( + RegExp.LAMBDA_LISTENER, + output_includes={'functional'}, + android_output_includes={ + self._context.runtime_include('verify_and_run.h') + }, + ios_output_includes={ + self._context.runtime_include('verify_and_run.h') + } + ) + + # listener rule + self._create_or_rule( + rules={ + self._create_reg_exp_rule(RegExp.PLATFORM_INTERFACE), + self._create_reg_exp_rule(RegExp.LISTENER), + lambda_listener_rule + }, + ios_output_types={OutputType.IOS_PRIVATE_HEADER, OutputType.IOS_BINDING_SOURCE}, + android_output_types={OutputType.ANDROID_HEADER, OutputType.ANDROID_SOURCE}, + output_includes={'memory'}, + android_output_includes={ + 'string', + self._context.runtime_include('verify_and_run.h') + }, + ios_output_includes={ + self._context.runtime_include('verify_and_run.h') + } + ) + + if self._context.unit.enabled("MAPS_MOBILE_USE_STD_VARIANT"): + variant_header = 'variant' + variant_serialization_header = self.context.runtime_include('serialization/variant.hpp') + else: + variant_header = 'boost/variant.hpp' + variant_serialization_header = 'boost/serialization/variant.hpp' + + variant_rule = self._create_reg_exp_rule( + RegExp.VARIANT, + ios_output_types={OutputType.IOS_PRIVATE_HEADER, OutputType.IOS_BINDING_SOURCE}, + output_includes={variant_header, 'boost/variant/recursive_wrapper.hpp'}, + ios_output_includes={ + self._context.runtime_include('bindings/ios/to_platform_fwd.h'), + self._context.runtime_include('bindings/ios/to_native_fwd.h'), + 'type_traits' + } + ) + + optional_rule = self._create_reg_exp_rule(RegExp.OPTIONAL, output_includes={'optional'}) + # int64 rule + self._create_reg_exp_rule(RegExp.INT_64, output_includes={'cstdint'}) + + string_rule = self._create_reg_exp_rule( + RegExp.STRING, + output_includes={ + 'string', + self._context.runtime_include('bindings/platform.h') + } + ) + + point_rule = self._create_reg_exp_rule( + RegExp.POINT, + output_includes={ + 'Eigen/Geometry', + self._context.runtime_include('bindings/point_traits.h') + }, + android_output_includes={ + self._context.runtime_include('bindings/android/point_to_native.h'), + self._context.runtime_include('bindings/android/point_to_platform.h') + }, + ios_output_includes={ + self._context.runtime_include('bindings/ios/point_to_native.h'), + self._context.runtime_include('bindings/ios/point_to_platform.h'), + 'UIKit/UIKit.h' + } + ) + + bytes_rule = self._create_reg_exp_rule(RegExp.BYTES, output_includes={'cstdint', 'vector'}) + + vector_rule = self._create_reg_exp_rule( + RegExp.VECTOR, + output_includes={ + 'memory', + self._context.runtime_include('bindings/platform.h') + }, + android_output_includes={ + self._context.runtime_include('bindings/android/vector_to_native.h'), + self._context.runtime_include('bindings/android/vector_to_platform.h') + }, + ios_output_includes={ + self._context.runtime_include('bindings/ios/vector_to_native.h'), + self._context.runtime_include('bindings/ios/vector_to_platform.h') + } + ) + + dictionary_rule = self._create_reg_exp_rule( + RegExp.DICTIONARY, + output_includes={ + 'memory', + self._context.runtime_include('bindings/platform.h') + }, + android_output_includes={ + self._context.runtime_include('bindings/android/dictionary_to_native.h'), + self._context.runtime_include('bindings/android/dictionary_to_platform.h') + }, + ios_output_includes={ + self._context.runtime_include('bindings/ios/dictionary_to_native.h'), + self._context.runtime_include('bindings/ios/dictionary_to_platform.h') + } + ) + + # any rule + self._create_reg_exp_rule( + RegExp.ANY, + output_includes={ + 'boost/any.hpp', + self._context.runtime_include('bindings/platform.h') + } + ) + + # any_collection rule + self._create_reg_exp_rule( + RegExp.ANY_COLLECTION, + output_includes={ + self._context.runtime_include('any/collection.h') + }, + android_output_includes={ + self._context.runtime_include('any/android/to_native.h'), + self._context.runtime_include('any/android/to_platform.h') + }, + ios_output_includes={ + self._context.runtime_objc_import('Collection.h'), + self._context.runtime_include('any/ios/to_native.h'), + self._context.runtime_include('any/ios/to_platform.h') + } + ) + + time_rule = self._create_reg_exp_rule( + RegExp.TIME, + output_includes={ + self._context.runtime_include('time.h') + } + ) + + # bitmap rule + self._create_reg_exp_rule( + RegExp.BITMAP, + output_includes={ + self._context.runtime_include('platform_bitmap.h') + }, + ios_output_includes={'UIKit/UIKit.h'} + ) + + # image_provider rule + self._create_reg_exp_rule( + RegExp.IMAGE_PROVIDER, + output_includes={ + self._context.runtime_include('image/image_provider.h') + }, + android_output_includes={ + self._context.runtime_include('image/android/image_provider_binding.h') + }, + ios_output_includes={ + self._context.runtime_include('image/ios/image_provider_binding.h'), + 'UIKit/UIKit.h' + } + ) + + # animated_image_provider rule + self._create_reg_exp_rule( + RegExp.ANIMATED_IMAGE_PROVIDER, + output_includes={ + self._context.runtime_include('image/animated_image_provider.h') + }, + android_output_includes={ + self._context.runtime_include('image/android/animated_image_provider_binding.h') + }, + ios_output_includes={ + self._context.runtime_include('image/ios/animated_image_provider_binding.h'), + self._context.runtime_objc_import('AnimatedImageProvider.h') + } + ) + + if not unit.enabled('MAPS_MOBILE_PUBLIC_API'): + # model_provider rule + self._create_reg_exp_rule( + RegExp.MODEL_PROVIDER, + output_includes={ + self._context.runtime_include('model/model_provider.h') + }, + android_output_includes={ + self._context.runtime_include('model/android/model_provider_binding.h') + }, + ios_output_includes={ + self._context.runtime_include('model/ios/model_provider_binding.h'), + self._context.runtime_objc_import('ModelProvider.h') + } + ) + + # animated_model_provider rule + self._create_reg_exp_rule( + RegExp.ANIMATED_MODEL_PROVIDER, + output_includes={ + self._context.runtime_include('model/animated_model_provider.h') + }, + android_output_includes={ + self._context.runtime_include('model/android/animated_model_provider_binding.h') + }, + ios_output_includes={ + self._context.runtime_include('model/ios/animated_model_provider_binding.h'), + self._context.runtime_objc_import('AnimatedModelProvider.h') + } + ) + + # view_provider rule + self._create_reg_exp_rule( + RegExp.VIEW_PROVIDER, + output_includes={ + self._context.runtime_include('ui_view/view_provider.h') + }, + android_output_includes={ + self._context.runtime_include('ui_view/android/view_provider_binding.h') + }, + ios_output_includes={ + self._context.runtime_include('ui_view/ios/view_provider_binding.h'), + self._context.runtime_objc_import('ViewProvider.h') + } + ) + + # platform_view rule + self._create_reg_exp_rule( + RegExp.PLATFORM_VIEW, + output_includes={ + self._context.runtime_include('view/platform_view.h') + }, + android_output_includes={ + self._context.runtime_include('view/android/to_native.h') + }, + ios_output_includes={ + self._context.runtime_include('view/ios/to_native.h'), + self._context.runtime_objc_import('PlatformView_Fwd.h'), + self._context.runtime_objc_import('PlatformView_Private.h') + } + ) + + # type_dictionary rule + self._create_reg_exp_rule( + RegExp.TYPE_DICTIONARY, + output_includes={ + self._context.runtime_include('bindings/platform.h'), + self._context.runtime_include('bindings/type_dictionary.h') + }, + android_output_includes={ + self._context.runtime_include('bindings/android/type_dictionary_to_native.h'), + self._context.runtime_include('bindings/android/type_dictionary_to_platform.h') + }, + ios_output_includes={ + self._context.runtime_include('bindings/ios/type_dictionary_to_native.h'), + self._context.runtime_include('bindings/ios/type_dictionary_to_platform.h'), + self._context.runtime_objc_import('TypeDictionary.h'), + } + ) + + # color rule + self._create_reg_exp_rule( + RegExp.COLOR, + output_includes={ + self._context.runtime_include('color.h') + }, + ios_output_includes={'UIKit/UIKit.h'} + ) + + # error rule + self._create_reg_exp_rule( + RegExp.ERROR, + android_output_includes={ + self._context.runtime_include('android/make_error.h') + }, + ios_output_includes={ + self._context.runtime_include('ios/make_error.h') + } + ) + + explicit_serialization = self._context.unit.enabled('MAPS_MOBILE_EXPLICIT_SERIALIZATION_ONLY') + if explicit_serialization: + explicit_serialization_rule = self._create_reg_exp_rule(RegExp.SERIALIZABLE) + else: + explicit_serialization_rule = struct_rule + + self._serialization_rule = self._create_or_rule( + rules={ + explicit_serialization_rule, + variant_rule + }, + output_includes={ + 'boost/serialization/nvp.hpp', + self._context.runtime_include('serialization/ptr.h'), + self._context.runtime_include('bindings/internal/archive_generator.h'), + self._context.runtime_include('bindings/internal/archive_reader.h'), + self._context.runtime_include('bindings/internal/archive_writer.h') + } + ) + + # point serialization rule + self._create_serialization_rule( + point_rule, + self._context.runtime_include('serialization/math.h') + ) + + # optional serialization rule + self._create_serialization_rule( + optional_rule, + self._context.runtime_include('serialization/serialization_std.h') + ) + + # bridged struct serialization rule + self._create_serialization_rule( + bridged_struct_rule, + self._context.runtime_include('any/guid_registration.h') + ) + + # time serialization rule + self._create_serialization_rule( + time_rule, + self._context.runtime_include('serialization/chrono.h') + ) + + # string serialization rule + self._create_serialization_rule( + string_rule, + 'boost/serialization/string.hpp' + ) + + # bytes serialization rule + self._create_serialization_rule( + bytes_rule, + 'boost/serialization/vector.hpp' + ) + + # vector serialization rule + self._create_serialization_rule( + vector_rule, + 'boost/serialization/vector.hpp' + ) + + # dictionary serialization rule + self._create_serialization_rule( + dictionary_rule, + 'boost/serialization/map.hpp' + ) + + # variant serialization rule + self._create_serialization_rule( + variant_rule, + variant_serialization_header + ) + + def _create_reg_exp_rule(self, reg_exp_string, *args, **kwargs): + rule = RegExpRule(reg_exp_string, self._context, *args, **kwargs) + self._rules.add(rule) + return rule + + def _create_or_rule(self, rules, *args, **kwargs): + rule = OrRule(rules, self._context, *args, **kwargs) + self._rules.add(rule) + return rule + + def _create_and_rule(self, rules, *args, **kwargs): + rule = AndRule(rules, self._context, *args, **kwargs) + self._rules.add(rule) + return rule + + def _create_serialization_rule(self, additional_rule, serialization_header): + rule = self._create_and_rule( + rules={ + self._serialization_rule, + additional_rule + }, + output_includes={ + serialization_header + } + ) + return rule + + def _split_and_remove_comments(self, input_file): + inside_comment = False + for line in input_file: + current_line = line + + if inside_comment: + closing_index = current_line.find("*/") + if closing_index == -1: + continue + current_line = current_line[closing_index + 2:] + inside_comment = False + + oneline_index = current_line.find("//") + if oneline_index != -1: + current_line = current_line[:oneline_index] + + opening_index = current_line.find("/*") + while opening_index != -1: + closing_index = current_line.find("*/") + if closing_index == -1: + current_line = current_line[:opening_index] + inside_comment = True + else: + current_line = current_line[:opening_index] + current_line[closing_index + 2:] + opening_index = current_line.find("/*") + + yield current_line + + def _should_add_to_output_includes(self, output_type): + return (self._context.add_generated_output_includes and + self._context.output_name_generator.is_header(output_type)) + + def process_files(self): + for file_path in self._context.file_paths: + self._context.output_name_generator = OutputNameGenerator(file_path, self._context.frameworks) + + for rule in self._rules: + rule.start_file(file_path) + + with open(os.path.join(self._resolved_idl_dir, file_path), 'r') as f: + for line in self._split_and_remove_comments(f): + for rule in self._rules: + rule.process_line(line) + + for rule in self._rules: + for output_type in rule.get_output_types(): + self._outputs.add(self._context.output_name_generator.generate_path(output_type)) + + if self._should_add_to_output_includes(output_type): + self._output_includes.add(self._context.output_name_generator.generate_name(output_type)) + + self._output_includes.update(rule.get_output_includes()) + + def get_outputs(self): + return self._outputs + + def get_output_includes(self): + return self._output_includes + + +def process_files(unit, file_paths): + frameworks = _read_frameworks(unit) + + processor = IdlFileProcessor(unit, frameworks, file_paths) + processor.process_files() + outputs = processor.get_outputs() + output_includes = processor.get_output_includes() + + return (outputs, output_includes) + + +def on_process_maps_mobile_idl(unit, *args): + if not unit.enabled('MAPSMOBI_BUILD_TARGET'): + return + + idl_files, kwds = sort_by_keywords({'FILTER': -1, 'FILTER_OUT': -1, 'GLOBAL_OUTPUTS': 0}, args) + + if len(idl_files) == 0: + return + + is_global_outputs = 'GLOBAL_OUTPUTS' in kwds + filter_in = kwds.get('FILTER', []) + filter_out = kwds.get('FILTER_OUT', []) + + is_java_idl = unit.enabled("JAVA_IDL") + + outputs, output_includes = process_files(unit, idl_files) + + if filter_in: + outputs = [o for o in outputs if any([o.endswith(x) for x in filter_in])] + if filter_out: + outputs = [o for o in outputs if not any([o.endswith(x) for x in filter_out])] + + if len(outputs) == 0 and not is_java_idl: + return + + base_out_dir = '${{ARCADIA_BUILD_ROOT}}/{}'.format(unit.path()[3:]) + unit.onaddincl(['GLOBAL', '{}/include'.format(base_out_dir)]) + + include_dirs = _get_appended_values(unit, 'MAPKIT_IDL_INCLUDES') + include_dirs.append(unit.path()[3:]) + + framework_dir = unit.get('MAPKIT_IDL_FRAMEWORK') + + extra_inputs = unit.get('MAPKIT_IDL_EXTRA_INPUTS').split(' ') + + idl_args = [] + idl_args.extend(['OUT_BASE_ROOT', base_out_dir, 'OUT_ANDROID_ROOT', base_out_dir, 'OUT_IOS_ROOT', base_out_dir]) + + if framework_dir: + idl_args.extend(['FRAMEWORK_DIRS', framework_dir]) + + if include_dirs: + idl_args.append('INCLUDES') + idl_args.extend(include_dirs) + + idl_args.append('IN') + idl_args.extend(idl_files) + if extra_inputs: + idl_args.extend(extra_inputs) + + if not is_java_idl: + sorted_outputs = sorted(outputs) + global_outputs = [] + non_global_outputs = [] + if is_global_outputs: + global_outputs = [x for x in sorted_outputs if x.endswith(('.cpp', '.m', '.mm'))] + non_global_outputs = sorted(set(outputs) - set(global_outputs)) + else: + non_global_outputs = sorted_outputs + + if global_outputs: + idl_args.append('OUT_NOAUTO') + idl_args.extend(global_outputs) + unit.onglobal_srcs(global_outputs) + + if non_global_outputs: + idl_args.append('OUT') + idl_args.extend(non_global_outputs) + + idl_args.append('OUTPUT_INCLUDES') + idl_args.extend(sorted(set(output_includes) - set(outputs))) + + idl_args.append('IDLS') + idl_args.extend(idl_files) + + if is_java_idl: + unit.on_run_idl_tool_java(idl_args) + else: + unit.on_run_idl_tool(idl_args) diff --git a/build/plugins/ya.make b/build/plugins/ya.make index 1bde4b81bf..e9ca97626d 100644 --- a/build/plugins/ya.make +++ b/build/plugins/ya.make @@ -5,6 +5,7 @@ PY2_LIBRARY() PY_SRCS( code_generator.py ssqls.py + maps_mobile_idl.py _common.py _requirements.py diff --git a/build/scripts/link_jsrc.py b/build/scripts/link_jsrc.py new file mode 100644 index 0000000000..feae72fe4e --- /dev/null +++ b/build/scripts/link_jsrc.py @@ -0,0 +1,27 @@ +import argparse +import tarfile + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument('--input', nargs='*') + parser.add_argument('--output', required=True) + + return parser.parse_args() + + +def main(): + args = parse_args() + + with tarfile.open(args.output, 'w') as dest: + for jsrc in [j for j in args.input if j.endswith('.jsrc')]: + with tarfile.open(jsrc, 'r') as src: + for item in [m for m in src.getmembers() if m.name != '']: + if item.isdir(): + dest.addfile(item) + else: + dest.addfile(item, src.extractfile(item)) + + +if __name__ == '__main__': + main() |