1*9e965d6fSRomain Jobredeaux# Copyright 2018 The Bazel Authors. All rights reserved. 2*9e965d6fSRomain Jobredeaux# 3*9e965d6fSRomain Jobredeaux# Licensed under the Apache License, Version 2.0 (the "License"); 4*9e965d6fSRomain Jobredeaux# you may not use this file except in compliance with the License. 5*9e965d6fSRomain Jobredeaux# You may obtain a copy of the License at 6*9e965d6fSRomain Jobredeaux# 7*9e965d6fSRomain Jobredeaux# http://www.apache.org/licenses/LICENSE-2.0 8*9e965d6fSRomain Jobredeaux# 9*9e965d6fSRomain Jobredeaux# Unless required by applicable law or agreed to in writing, software 10*9e965d6fSRomain Jobredeaux# distributed under the License is distributed on an "AS IS" BASIS, 11*9e965d6fSRomain Jobredeaux# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*9e965d6fSRomain Jobredeaux# See the License for the specific language governing permissions and 13*9e965d6fSRomain Jobredeaux# limitations under the License. 14*9e965d6fSRomain Jobredeaux 15*9e965d6fSRomain Jobredeaux"""Bazel Android Data Binding.""" 16*9e965d6fSRomain Jobredeaux 17*9e965d6fSRomain Jobredeauxload(":utils.bzl", "ANDROID_TOOLCHAIN_TYPE", _utils = "utils") 18*9e965d6fSRomain Jobredeaux 19*9e965d6fSRomain Jobredeaux# Data Binding context attributes. 20*9e965d6fSRomain Jobredeaux_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS = \ 21*9e965d6fSRomain Jobredeaux "java_annotation_processor_additional_inputs" 22*9e965d6fSRomain Jobredeaux_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS = \ 23*9e965d6fSRomain Jobredeaux "java_annotation_processor_additional_outputs" 24*9e965d6fSRomain Jobredeaux_JAVA_PLUGINS = "java_plugins" 25*9e965d6fSRomain Jobredeaux_JAVA_SRCS = "java_srcs" 26*9e965d6fSRomain Jobredeaux_JAVAC_OPTS = "javac_opts" 27*9e965d6fSRomain Jobredeaux_PROVIDERS = "providers" 28*9e965d6fSRomain Jobredeaux 29*9e965d6fSRomain JobredeauxDataBindingContextInfo = provider( 30*9e965d6fSRomain Jobredeaux doc = "Contains data from processing Android Data Binding.", 31*9e965d6fSRomain Jobredeaux fields = { 32*9e965d6fSRomain Jobredeaux _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS: ( 33*9e965d6fSRomain Jobredeaux "Additional inputs required by the Java annotation processor." 34*9e965d6fSRomain Jobredeaux ), 35*9e965d6fSRomain Jobredeaux _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS: ( 36*9e965d6fSRomain Jobredeaux "Additional outputs produced by the Java annotation processor." 37*9e965d6fSRomain Jobredeaux ), 38*9e965d6fSRomain Jobredeaux _JAVA_PLUGINS: "Data Binding Java annotation processor", 39*9e965d6fSRomain Jobredeaux _JAVA_SRCS: "Java sources required by the Java annotation processor.", 40*9e965d6fSRomain Jobredeaux _JAVAC_OPTS: ( 41*9e965d6fSRomain Jobredeaux "Additional Javac opts required by the Java annotation processor." 42*9e965d6fSRomain Jobredeaux ), 43*9e965d6fSRomain Jobredeaux _PROVIDERS: "The list of all providers to propagate.", 44*9e965d6fSRomain Jobredeaux }, 45*9e965d6fSRomain Jobredeaux) 46*9e965d6fSRomain Jobredeaux 47*9e965d6fSRomain Jobredeaux# Path used when resources have not been defined. 48*9e965d6fSRomain Jobredeaux_NO_RESOURCES_PATH = "/tmp/no_resources" 49*9e965d6fSRomain Jobredeaux 50*9e965d6fSRomain Jobredeauxdef _copy_annotation_file(ctx, output_dir, annotation_template): 51*9e965d6fSRomain Jobredeaux annotation_out = ctx.actions.declare_file( 52*9e965d6fSRomain Jobredeaux output_dir + "/android/databinding/layouts/DataBindingInfo.java", 53*9e965d6fSRomain Jobredeaux ) 54*9e965d6fSRomain Jobredeaux _utils.copy_file(ctx, annotation_template, annotation_out) 55*9e965d6fSRomain Jobredeaux return annotation_out 56*9e965d6fSRomain Jobredeaux 57*9e965d6fSRomain Jobredeauxdef _gen_sources(ctx, output_dir, java_package, deps, layout_info, data_binding_exec): 58*9e965d6fSRomain Jobredeaux class_info = ctx.actions.declare_file(output_dir + "class-info.zip") 59*9e965d6fSRomain Jobredeaux srcjar = ctx.actions.declare_file(output_dir + "baseClassSrc.srcjar") 60*9e965d6fSRomain Jobredeaux 61*9e965d6fSRomain Jobredeaux args = ctx.actions.args() 62*9e965d6fSRomain Jobredeaux args.add("-layoutInfoFiles", layout_info) 63*9e965d6fSRomain Jobredeaux args.add("-package", java_package) 64*9e965d6fSRomain Jobredeaux args.add("-classInfoOut", class_info) 65*9e965d6fSRomain Jobredeaux args.add("-sourceOut", srcjar) 66*9e965d6fSRomain Jobredeaux args.add("-zipSourceOutput", "true") 67*9e965d6fSRomain Jobredeaux args.add("-useAndroidX", "false") 68*9e965d6fSRomain Jobredeaux 69*9e965d6fSRomain Jobredeaux if deps: 70*9e965d6fSRomain Jobredeaux if type(deps[0].class_infos) == "depset": 71*9e965d6fSRomain Jobredeaux class_infos = depset(transitive = [info.class_infos for info in deps]) 72*9e965d6fSRomain Jobredeaux inputs = depset(direct = [layout_info], transitive = [class_infos]) 73*9e965d6fSRomain Jobredeaux elif type(deps[0].class_infos) == "list": 74*9e965d6fSRomain Jobredeaux class_infos = [] 75*9e965d6fSRomain Jobredeaux for info in deps: 76*9e965d6fSRomain Jobredeaux class_infos.extend(info.class_infos) 77*9e965d6fSRomain Jobredeaux inputs = class_infos + [layout_info] 78*9e965d6fSRomain Jobredeaux else: 79*9e965d6fSRomain Jobredeaux fail("Expected list or depset. Got %s" % type(deps[0].class_infos)) 80*9e965d6fSRomain Jobredeaux else: 81*9e965d6fSRomain Jobredeaux class_infos = [] 82*9e965d6fSRomain Jobredeaux inputs = [layout_info] 83*9e965d6fSRomain Jobredeaux 84*9e965d6fSRomain Jobredeaux args.add_all(class_infos, before_each = "-dependencyClassInfoList") 85*9e965d6fSRomain Jobredeaux 86*9e965d6fSRomain Jobredeaux ctx.actions.run( 87*9e965d6fSRomain Jobredeaux executable = data_binding_exec, 88*9e965d6fSRomain Jobredeaux arguments = ["GEN_BASE_CLASSES", args], 89*9e965d6fSRomain Jobredeaux inputs = inputs, 90*9e965d6fSRomain Jobredeaux outputs = [class_info, srcjar], 91*9e965d6fSRomain Jobredeaux mnemonic = "GenerateDataBindingBaseClasses", 92*9e965d6fSRomain Jobredeaux progress_message = ( 93*9e965d6fSRomain Jobredeaux "GenerateDataBindingBaseClasses %s" % class_info.short_path 94*9e965d6fSRomain Jobredeaux ), 95*9e965d6fSRomain Jobredeaux toolchain = ANDROID_TOOLCHAIN_TYPE, 96*9e965d6fSRomain Jobredeaux ) 97*9e965d6fSRomain Jobredeaux return srcjar, class_info 98*9e965d6fSRomain Jobredeaux 99*9e965d6fSRomain Jobredeauxdef _setup_dependent_lib_artifacts(ctx, output_dir, deps): 100*9e965d6fSRomain Jobredeaux # DataBinding requires files in very specific locations. 101*9e965d6fSRomain Jobredeaux # The following expand_template (copy actions) are moving the files 102*9e965d6fSRomain Jobredeaux # to the correct locations. 103*9e965d6fSRomain Jobredeaux dep_lib_artifacts = [] 104*9e965d6fSRomain Jobredeaux for info in deps: 105*9e965d6fSRomain Jobredeaux # Yes, DataBinding requires depsets iterations. 106*9e965d6fSRomain Jobredeaux for artifact in (info.transitive_br_files.to_list() + 107*9e965d6fSRomain Jobredeaux _utils.list_or_depset_to_list(info.setter_stores) + 108*9e965d6fSRomain Jobredeaux _utils.list_or_depset_to_list(info.class_infos)): 109*9e965d6fSRomain Jobredeaux # short_path might contain a parent directory reference if the 110*9e965d6fSRomain Jobredeaux # databinding artifact is from an external repository (e.g. an aar 111*9e965d6fSRomain Jobredeaux # from Maven). If that's the case, just remove the parent directory 112*9e965d6fSRomain Jobredeaux # reference, otherwise the "dependent-lib-artifacts" directory will 113*9e965d6fSRomain Jobredeaux # get removed by the "..". 114*9e965d6fSRomain Jobredeaux path = artifact.short_path 115*9e965d6fSRomain Jobredeaux if path.startswith("../"): 116*9e965d6fSRomain Jobredeaux path = path[3:] 117*9e965d6fSRomain Jobredeaux dep_lib_artifact = ctx.actions.declare_file( 118*9e965d6fSRomain Jobredeaux output_dir + "dependent-lib-artifacts/" + path, 119*9e965d6fSRomain Jobredeaux ) 120*9e965d6fSRomain Jobredeaux 121*9e965d6fSRomain Jobredeaux # Copy file to a location required by the DataBinding annotation 122*9e965d6fSRomain Jobredeaux # processor. 123*9e965d6fSRomain Jobredeaux # TODO(djwhang): Look into SymlinkAction. 124*9e965d6fSRomain Jobredeaux if artifact.is_directory: 125*9e965d6fSRomain Jobredeaux _utils.copy_dir(ctx, artifact, dep_lib_artifact) 126*9e965d6fSRomain Jobredeaux else: 127*9e965d6fSRomain Jobredeaux _utils.copy_file(ctx, artifact, dep_lib_artifact) 128*9e965d6fSRomain Jobredeaux dep_lib_artifacts.append(dep_lib_artifact) 129*9e965d6fSRomain Jobredeaux return dep_lib_artifacts 130*9e965d6fSRomain Jobredeaux 131*9e965d6fSRomain Jobredeauxdef _get_javac_opts( 132*9e965d6fSRomain Jobredeaux ctx, 133*9e965d6fSRomain Jobredeaux java_package, 134*9e965d6fSRomain Jobredeaux artifact_type, 135*9e965d6fSRomain Jobredeaux dependency_artifacts_dir, 136*9e965d6fSRomain Jobredeaux aar_out_dir, 137*9e965d6fSRomain Jobredeaux class_info_path, 138*9e965d6fSRomain Jobredeaux layout_info_path, 139*9e965d6fSRomain Jobredeaux deps): 140*9e965d6fSRomain Jobredeaux java_packages = [] 141*9e965d6fSRomain Jobredeaux for info in deps: 142*9e965d6fSRomain Jobredeaux for label_and_java_package in info.label_and_java_packages: 143*9e965d6fSRomain Jobredeaux java_packages.append(label_and_java_package.java_package) 144*9e965d6fSRomain Jobredeaux 145*9e965d6fSRomain Jobredeaux javac_opts = [] 146*9e965d6fSRomain Jobredeaux javac_opts.append("-Aandroid.databinding.dependencyArtifactsDir=" + 147*9e965d6fSRomain Jobredeaux dependency_artifacts_dir) 148*9e965d6fSRomain Jobredeaux javac_opts.append("-Aandroid.databinding.aarOutDir=" + aar_out_dir) 149*9e965d6fSRomain Jobredeaux javac_opts.append("-Aandroid.databinding.sdkDir=/not/used") 150*9e965d6fSRomain Jobredeaux javac_opts.append("-Aandroid.databinding.artifactType=" + artifact_type) 151*9e965d6fSRomain Jobredeaux javac_opts.append("-Aandroid.databinding.exportClassListOutFile=" + 152*9e965d6fSRomain Jobredeaux "/tmp/exported_classes") 153*9e965d6fSRomain Jobredeaux javac_opts.append("-Aandroid.databinding.modulePackage=" + java_package) 154*9e965d6fSRomain Jobredeaux javac_opts.append("-Aandroid.databinding.directDependencyPkgs=[%s]" % 155*9e965d6fSRomain Jobredeaux ",".join(java_packages)) 156*9e965d6fSRomain Jobredeaux 157*9e965d6fSRomain Jobredeaux # The minimum Android SDK compatible with this rule. 158*9e965d6fSRomain Jobredeaux # TODO(djwhang): This probably should be based on the actual min-sdk from 159*9e965d6fSRomain Jobredeaux # the manifest, or an appropriate rule attribute. 160*9e965d6fSRomain Jobredeaux javac_opts.append("-Aandroid.databinding.minApi=14") 161*9e965d6fSRomain Jobredeaux javac_opts.append("-Aandroid.databinding.enableV2=1") 162*9e965d6fSRomain Jobredeaux 163*9e965d6fSRomain Jobredeaux javac_opts.append("-Aandroid.databinding.classLogDir=" + class_info_path) 164*9e965d6fSRomain Jobredeaux javac_opts.append("-Aandroid.databinding.layoutInfoDir=" + layout_info_path) 165*9e965d6fSRomain Jobredeaux return javac_opts 166*9e965d6fSRomain Jobredeaux 167*9e965d6fSRomain Jobredeauxdef _process( 168*9e965d6fSRomain Jobredeaux ctx, 169*9e965d6fSRomain Jobredeaux resources_ctx = None, 170*9e965d6fSRomain Jobredeaux defines_resources = False, 171*9e965d6fSRomain Jobredeaux enable_data_binding = False, 172*9e965d6fSRomain Jobredeaux java_package = None, 173*9e965d6fSRomain Jobredeaux layout_info = None, 174*9e965d6fSRomain Jobredeaux artifact_type = "LIBRARY", 175*9e965d6fSRomain Jobredeaux deps = [], 176*9e965d6fSRomain Jobredeaux exports = [], 177*9e965d6fSRomain Jobredeaux data_binding_exec = None, 178*9e965d6fSRomain Jobredeaux data_binding_annotation_processor = None, 179*9e965d6fSRomain Jobredeaux data_binding_annotation_template = None): 180*9e965d6fSRomain Jobredeaux """Processes Android Data Binding. 181*9e965d6fSRomain Jobredeaux 182*9e965d6fSRomain Jobredeaux Args: 183*9e965d6fSRomain Jobredeaux ctx: The context. 184*9e965d6fSRomain Jobredeaux resources_ctx: The Android Resources context. 185*9e965d6fSRomain Jobredeaux defines_resources: boolean. Determines whether resources were defined. 186*9e965d6fSRomain Jobredeaux enable_data_binding: boolean. Determines whether Data Binding should be 187*9e965d6fSRomain Jobredeaux enabled. 188*9e965d6fSRomain Jobredeaux java_package: String. The Java package. 189*9e965d6fSRomain Jobredeaux layout_info: A file. The layout-info zip file. 190*9e965d6fSRomain Jobredeaux artifact_type: String. Either LIBRARY or APPLICATION. 191*9e965d6fSRomain Jobredeaux deps: sequence of DataBindingV2Info providers. A list of deps. Optional. 192*9e965d6fSRomain Jobredeaux exports: sequence of DataBindingV2Info providers. A list of exports. 193*9e965d6fSRomain Jobredeaux Optional. 194*9e965d6fSRomain Jobredeaux data_binding_exec: The DataBinding executable. 195*9e965d6fSRomain Jobredeaux data_binding_annotation_processor: JavaInfo. The JavaInfo for the 196*9e965d6fSRomain Jobredeaux annotation processor. 197*9e965d6fSRomain Jobredeaux data_binding_annotation_template: A file. Used to generate data binding 198*9e965d6fSRomain Jobredeaux classes. 199*9e965d6fSRomain Jobredeaux 200*9e965d6fSRomain Jobredeaux Returns: 201*9e965d6fSRomain Jobredeaux A DataBindingContextInfo provider. 202*9e965d6fSRomain Jobredeaux """ 203*9e965d6fSRomain Jobredeaux 204*9e965d6fSRomain Jobredeaux if artifact_type not in ["LIBRARY", "APPLICATION"]: 205*9e965d6fSRomain Jobredeaux fail("Unexpected artifact type: " + artifact_type) 206*9e965d6fSRomain Jobredeaux 207*9e965d6fSRomain Jobredeaux # TODO(b/154513292): Clean up bad usages of context objects. 208*9e965d6fSRomain Jobredeaux if resources_ctx: 209*9e965d6fSRomain Jobredeaux defines_resources = resources_ctx.defines_resources 210*9e965d6fSRomain Jobredeaux 211*9e965d6fSRomain Jobredeaux # The Android Data Binding context object. 212*9e965d6fSRomain Jobredeaux db_info = { 213*9e965d6fSRomain Jobredeaux _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS: [], 214*9e965d6fSRomain Jobredeaux _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS: [], 215*9e965d6fSRomain Jobredeaux _JAVA_PLUGINS: [], 216*9e965d6fSRomain Jobredeaux _JAVA_SRCS: [], 217*9e965d6fSRomain Jobredeaux _JAVAC_OPTS: [], 218*9e965d6fSRomain Jobredeaux _PROVIDERS: [], 219*9e965d6fSRomain Jobredeaux } 220*9e965d6fSRomain Jobredeaux 221*9e965d6fSRomain Jobredeaux if not enable_data_binding: 222*9e965d6fSRomain Jobredeaux db_info[_PROVIDERS] = [ 223*9e965d6fSRomain Jobredeaux DataBindingV2Info( 224*9e965d6fSRomain Jobredeaux databinding_v2_providers_in_deps = deps, 225*9e965d6fSRomain Jobredeaux databinding_v2_providers_in_exports = exports, 226*9e965d6fSRomain Jobredeaux ), 227*9e965d6fSRomain Jobredeaux ] 228*9e965d6fSRomain Jobredeaux return struct(**db_info) 229*9e965d6fSRomain Jobredeaux 230*9e965d6fSRomain Jobredeaux output_dir = "databinding/%s/" % ctx.label.name 231*9e965d6fSRomain Jobredeaux 232*9e965d6fSRomain Jobredeaux db_info[_JAVA_SRCS].append(_copy_annotation_file( 233*9e965d6fSRomain Jobredeaux ctx, 234*9e965d6fSRomain Jobredeaux output_dir, 235*9e965d6fSRomain Jobredeaux data_binding_annotation_template, 236*9e965d6fSRomain Jobredeaux )) 237*9e965d6fSRomain Jobredeaux db_info[_JAVA_PLUGINS].append(data_binding_annotation_processor) 238*9e965d6fSRomain Jobredeaux 239*9e965d6fSRomain Jobredeaux br_out = None 240*9e965d6fSRomain Jobredeaux setter_store_out = None 241*9e965d6fSRomain Jobredeaux class_info = None 242*9e965d6fSRomain Jobredeaux if defines_resources: 243*9e965d6fSRomain Jobredeaux # Outputs of the Data Binding annotation processor. 244*9e965d6fSRomain Jobredeaux br_out = ctx.actions.declare_file( 245*9e965d6fSRomain Jobredeaux output_dir + "bin-files/%s-br.bin" % java_package, 246*9e965d6fSRomain Jobredeaux ) 247*9e965d6fSRomain Jobredeaux db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS].append(br_out) 248*9e965d6fSRomain Jobredeaux setter_store_out = ctx.actions.declare_file( 249*9e965d6fSRomain Jobredeaux output_dir + "bin-files/%s-setter_store.json" % java_package, 250*9e965d6fSRomain Jobredeaux ) 251*9e965d6fSRomain Jobredeaux db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS].append( 252*9e965d6fSRomain Jobredeaux setter_store_out, 253*9e965d6fSRomain Jobredeaux ) 254*9e965d6fSRomain Jobredeaux 255*9e965d6fSRomain Jobredeaux srcjar, class_info = _gen_sources( 256*9e965d6fSRomain Jobredeaux ctx, 257*9e965d6fSRomain Jobredeaux output_dir, 258*9e965d6fSRomain Jobredeaux java_package, 259*9e965d6fSRomain Jobredeaux deps, 260*9e965d6fSRomain Jobredeaux layout_info, 261*9e965d6fSRomain Jobredeaux data_binding_exec, 262*9e965d6fSRomain Jobredeaux ) 263*9e965d6fSRomain Jobredeaux db_info[_JAVA_SRCS].append(srcjar) 264*9e965d6fSRomain Jobredeaux db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS].append(class_info) 265*9e965d6fSRomain Jobredeaux db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS].append( 266*9e965d6fSRomain Jobredeaux layout_info, 267*9e965d6fSRomain Jobredeaux ) 268*9e965d6fSRomain Jobredeaux 269*9e965d6fSRomain Jobredeaux dep_lib_artifacts = _setup_dependent_lib_artifacts(ctx, output_dir, deps) 270*9e965d6fSRomain Jobredeaux db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS].extend( 271*9e965d6fSRomain Jobredeaux dep_lib_artifacts, 272*9e965d6fSRomain Jobredeaux ) 273*9e965d6fSRomain Jobredeaux 274*9e965d6fSRomain Jobredeaux db_info[_JAVAC_OPTS] = _get_javac_opts( 275*9e965d6fSRomain Jobredeaux ctx, 276*9e965d6fSRomain Jobredeaux java_package, 277*9e965d6fSRomain Jobredeaux artifact_type, 278*9e965d6fSRomain Jobredeaux ( 279*9e965d6fSRomain Jobredeaux br_out.path.rpartition(br_out.short_path)[0] + 280*9e965d6fSRomain Jobredeaux ctx.label.package + 281*9e965d6fSRomain Jobredeaux "/" + 282*9e965d6fSRomain Jobredeaux output_dir + 283*9e965d6fSRomain Jobredeaux "dependent-lib-artifacts" 284*9e965d6fSRomain Jobredeaux ), 285*9e965d6fSRomain Jobredeaux br_out.dirname, 286*9e965d6fSRomain Jobredeaux class_info.path if class_info else _NO_RESOURCES_PATH, 287*9e965d6fSRomain Jobredeaux layout_info.path if layout_info else _NO_RESOURCES_PATH, 288*9e965d6fSRomain Jobredeaux deps, 289*9e965d6fSRomain Jobredeaux ) 290*9e965d6fSRomain Jobredeaux 291*9e965d6fSRomain Jobredeaux db_info[_PROVIDERS] = [ 292*9e965d6fSRomain Jobredeaux DataBindingV2Info( 293*9e965d6fSRomain Jobredeaux setter_store_file = setter_store_out, 294*9e965d6fSRomain Jobredeaux class_info_file = class_info, 295*9e965d6fSRomain Jobredeaux br_file = br_out, 296*9e965d6fSRomain Jobredeaux label = str(ctx.label), 297*9e965d6fSRomain Jobredeaux java_package = java_package, 298*9e965d6fSRomain Jobredeaux databinding_v2_providers_in_deps = deps, 299*9e965d6fSRomain Jobredeaux databinding_v2_providers_in_exports = exports, 300*9e965d6fSRomain Jobredeaux ), 301*9e965d6fSRomain Jobredeaux ] 302*9e965d6fSRomain Jobredeaux 303*9e965d6fSRomain Jobredeaux return DataBindingContextInfo(**db_info) 304*9e965d6fSRomain Jobredeaux 305*9e965d6fSRomain Jobredeauxdata_binding = struct( 306*9e965d6fSRomain Jobredeaux process = _process, 307*9e965d6fSRomain Jobredeaux) 308