aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortrivias <trivias@yandex-team.com>2023-03-20 18:05:59 +0300
committertrivias <trivias@yandex-team.com>2023-03-20 18:05:59 +0300
commit30574d399b041f91ad5143e57acca97636a54085 (patch)
treedd7696fff1e47c9d5cc243f4b0c1899ea6df925d
parente0305489c73afe7196823399dc37f5d8031c0ca2 (diff)
downloadydb-30574d399b041f91ad5143e57acca97636a54085.tar.gz
Idl plugin for ymake
-rw-r--r--build/conf/project_specific/maps/mapkit.conf55
-rw-r--r--build/plugins/maps_mobile_idl.py1160
-rw-r--r--build/plugins/ya.make1
-rw-r--r--build/scripts/link_jsrc.py27
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()