1*9e965d6fSRomain Jobredeaux# Copyright 2020 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"""Common implementation for processing pipelines.""" 16*9e965d6fSRomain Jobredeaux 17*9e965d6fSRomain JobredeauxPROVIDERS = "providers" 18*9e965d6fSRomain JobredeauxVALIDATION_OUTPUTS = "validation_outputs" 19*9e965d6fSRomain Jobredeaux 20*9e965d6fSRomain Jobredeaux# TODO(djwhang): When a provider type can be retrieved from a Starlark provider 21*9e965d6fSRomain Jobredeaux# ProviderInfo is necessary. Once this is possible, processor methods can have a 22*9e965d6fSRomain Jobredeaux# uniform method signature foo(ctx, target_ctx) where we can pull the provider 23*9e965d6fSRomain Jobredeaux# off the target_ctx using the provider type. 24*9e965d6fSRomain Jobredeaux# 25*9e965d6fSRomain Jobredeaux# Yes, this effectively leads to producing a build rule like system within a 26*9e965d6fSRomain Jobredeaux# build rule, rather than resorting to rule based composition. 27*9e965d6fSRomain JobredeauxProviderInfo = provider( 28*9e965d6fSRomain Jobredeaux "Stores metadata about the actual Starlark provider returned.", 29*9e965d6fSRomain Jobredeaux fields = dict( 30*9e965d6fSRomain Jobredeaux name = "The type of the provider", 31*9e965d6fSRomain Jobredeaux value = "The actual provider", 32*9e965d6fSRomain Jobredeaux runfiles = "Runfiles to pass to the DefaultInfo provider", 33*9e965d6fSRomain Jobredeaux ), 34*9e965d6fSRomain Jobredeaux) 35*9e965d6fSRomain Jobredeaux 36*9e965d6fSRomain Jobredeaux_ProcessingPipelineInfo = provider( 37*9e965d6fSRomain Jobredeaux "Stores functions that forms a rule's implementation.", 38*9e965d6fSRomain Jobredeaux fields = dict( 39*9e965d6fSRomain Jobredeaux processors = "Ordered dictionary of processing functions.", 40*9e965d6fSRomain Jobredeaux finalize = "Function to form the final providers to propagate.", 41*9e965d6fSRomain Jobredeaux ), 42*9e965d6fSRomain Jobredeaux) 43*9e965d6fSRomain Jobredeaux 44*9e965d6fSRomain Jobredeauxdef _make_processing_pipeline(processors = dict(), finalize = None): 45*9e965d6fSRomain Jobredeaux """Creates the combined processing pipeline. 46*9e965d6fSRomain Jobredeaux 47*9e965d6fSRomain Jobredeaux Args: 48*9e965d6fSRomain Jobredeaux processors: Ordered dictionary of processing functions. 49*9e965d6fSRomain Jobredeaux finalize: Function to form the final providers to propagate. 50*9e965d6fSRomain Jobredeaux 51*9e965d6fSRomain Jobredeaux Returns: 52*9e965d6fSRomain Jobredeaux A _ProcessingPipelineInfo provider. 53*9e965d6fSRomain Jobredeaux """ 54*9e965d6fSRomain Jobredeaux return _ProcessingPipelineInfo( 55*9e965d6fSRomain Jobredeaux processors = processors, 56*9e965d6fSRomain Jobredeaux finalize = finalize, 57*9e965d6fSRomain Jobredeaux ) 58*9e965d6fSRomain Jobredeaux 59*9e965d6fSRomain Jobredeauxdef _run(ctx, java_package, processing_pipeline): 60*9e965d6fSRomain Jobredeaux """Runs the processing pipeline and populates the target context. 61*9e965d6fSRomain Jobredeaux 62*9e965d6fSRomain Jobredeaux Args: 63*9e965d6fSRomain Jobredeaux ctx: The context. 64*9e965d6fSRomain Jobredeaux java_package: The java package resolved from the target's path 65*9e965d6fSRomain Jobredeaux or the custom_package attr. 66*9e965d6fSRomain Jobredeaux processing_pipeline: The _ProcessingPipelineInfo provider for this target. 67*9e965d6fSRomain Jobredeaux 68*9e965d6fSRomain Jobredeaux Returns: 69*9e965d6fSRomain Jobredeaux The output of the _ProcessingPipelineInfo.finalize function. 70*9e965d6fSRomain Jobredeaux """ 71*9e965d6fSRomain Jobredeaux target_ctx = dict( 72*9e965d6fSRomain Jobredeaux java_package = java_package, 73*9e965d6fSRomain Jobredeaux providers = [], 74*9e965d6fSRomain Jobredeaux validation_outputs = [], 75*9e965d6fSRomain Jobredeaux runfiles = ctx.runfiles(), 76*9e965d6fSRomain Jobredeaux ) 77*9e965d6fSRomain Jobredeaux 78*9e965d6fSRomain Jobredeaux for execute in processing_pipeline.processors.values(): 79*9e965d6fSRomain Jobredeaux info = execute(ctx, **target_ctx) 80*9e965d6fSRomain Jobredeaux if info: 81*9e965d6fSRomain Jobredeaux if info.name in target_ctx: 82*9e965d6fSRomain Jobredeaux fail("%s provider already registered in target context" % info.name) 83*9e965d6fSRomain Jobredeaux target_ctx[info.name] = info.value 84*9e965d6fSRomain Jobredeaux target_ctx[PROVIDERS].extend(getattr(info.value, PROVIDERS, [])) 85*9e965d6fSRomain Jobredeaux target_ctx[VALIDATION_OUTPUTS].extend(getattr(info.value, VALIDATION_OUTPUTS, [])) 86*9e965d6fSRomain Jobredeaux if hasattr(info, "runfiles") and info.runfiles: 87*9e965d6fSRomain Jobredeaux target_ctx["runfiles"] = target_ctx["runfiles"].merge(info.runfiles) 88*9e965d6fSRomain Jobredeaux 89*9e965d6fSRomain Jobredeaux return processing_pipeline.finalize(ctx, **target_ctx) 90*9e965d6fSRomain Jobredeaux 91*9e965d6fSRomain Jobredeauxdef _prepend(processors, **new_processors): 92*9e965d6fSRomain Jobredeaux """Prepends processors in a given processing pipeline. 93*9e965d6fSRomain Jobredeaux 94*9e965d6fSRomain Jobredeaux Args: 95*9e965d6fSRomain Jobredeaux processors: The dictionary representing the processing pipeline. 96*9e965d6fSRomain Jobredeaux **new_processors: The processors to add where the key represents the 97*9e965d6fSRomain Jobredeaux name of the processor and value is the function pointer to the new 98*9e965d6fSRomain Jobredeaux processor. 99*9e965d6fSRomain Jobredeaux 100*9e965d6fSRomain Jobredeaux Returns: 101*9e965d6fSRomain Jobredeaux A dictionary which represents the new processing pipeline. 102*9e965d6fSRomain Jobredeaux """ 103*9e965d6fSRomain Jobredeaux updated_processors = dict() 104*9e965d6fSRomain Jobredeaux for name, processor in new_processors.items(): 105*9e965d6fSRomain Jobredeaux updated_processors[name] = processor 106*9e965d6fSRomain Jobredeaux 107*9e965d6fSRomain Jobredeaux for key in processors.keys(): 108*9e965d6fSRomain Jobredeaux updated_processors[key] = processors[key] 109*9e965d6fSRomain Jobredeaux 110*9e965d6fSRomain Jobredeaux return updated_processors 111*9e965d6fSRomain Jobredeaux 112*9e965d6fSRomain Jobredeauxdef _append(processors, **new_processors): 113*9e965d6fSRomain Jobredeaux """Appends processors in a given processing pipeline. 114*9e965d6fSRomain Jobredeaux 115*9e965d6fSRomain Jobredeaux Args: 116*9e965d6fSRomain Jobredeaux processors: The dictionary representing the processing pipeline. 117*9e965d6fSRomain Jobredeaux **new_processors: The processors to append where the key represents the 118*9e965d6fSRomain Jobredeaux name of the processor and value is the function pointer to the new 119*9e965d6fSRomain Jobredeaux processor. 120*9e965d6fSRomain Jobredeaux 121*9e965d6fSRomain Jobredeaux Returns: 122*9e965d6fSRomain Jobredeaux A dictionary which represents the new processing pipeline. 123*9e965d6fSRomain Jobredeaux """ 124*9e965d6fSRomain Jobredeaux updated_processors = dict(processors) 125*9e965d6fSRomain Jobredeaux for name, processor in new_processors.items(): 126*9e965d6fSRomain Jobredeaux updated_processors[name] = processor 127*9e965d6fSRomain Jobredeaux 128*9e965d6fSRomain Jobredeaux return updated_processors 129*9e965d6fSRomain Jobredeaux 130*9e965d6fSRomain Jobredeauxdef _replace(processors, **new_processors): 131*9e965d6fSRomain Jobredeaux """Replace processors in a given processing pipeline. 132*9e965d6fSRomain Jobredeaux 133*9e965d6fSRomain Jobredeaux Args: 134*9e965d6fSRomain Jobredeaux processors: The dictionary representing the processing pipeline. 135*9e965d6fSRomain Jobredeaux **new_processors: The processors to override where the key represents the 136*9e965d6fSRomain Jobredeaux name of the processor and value is the function pointer to the new 137*9e965d6fSRomain Jobredeaux processor. 138*9e965d6fSRomain Jobredeaux 139*9e965d6fSRomain Jobredeaux Returns: 140*9e965d6fSRomain Jobredeaux A dictionary which represents the new processing pipeline. 141*9e965d6fSRomain Jobredeaux """ 142*9e965d6fSRomain Jobredeaux updated_processors = dict(processors) 143*9e965d6fSRomain Jobredeaux for name, processor in new_processors.items(): 144*9e965d6fSRomain Jobredeaux if name not in processors: 145*9e965d6fSRomain Jobredeaux fail("Error, %s not found, unable to override." % name) 146*9e965d6fSRomain Jobredeaux 147*9e965d6fSRomain Jobredeaux # NOTE: Overwriting an existing value does not break iteration order. 148*9e965d6fSRomain Jobredeaux # However, if a new processor is being added that needs to be injected 149*9e965d6fSRomain Jobredeaux # between other processors, the processing pipeline dictionary will need 150*9e965d6fSRomain Jobredeaux # to be recreated. 151*9e965d6fSRomain Jobredeaux updated_processors[name] = processor 152*9e965d6fSRomain Jobredeaux 153*9e965d6fSRomain Jobredeaux return updated_processors 154*9e965d6fSRomain Jobredeaux 155*9e965d6fSRomain Jobredeauxprocessing_pipeline = struct( 156*9e965d6fSRomain Jobredeaux make_processing_pipeline = _make_processing_pipeline, 157*9e965d6fSRomain Jobredeaux run = _run, 158*9e965d6fSRomain Jobredeaux prepend = _prepend, 159*9e965d6fSRomain Jobredeaux append = _append, 160*9e965d6fSRomain Jobredeaux replace = _replace, 161*9e965d6fSRomain Jobredeaux) 162