/** * If the rule was created by a macro, this method sets the appropriate values for the attributes * generator_{name, function, location} and returns all attributes. * * <p>Otherwise, it returns the given attributes without any changes. */ private static AttributesAndLocation generatorAttributesForMacros( BuildLangTypedAttributeValuesMap args, @Nullable Environment env, Location location, Label label) { // Returns the original arguments if a) there is only the rule itself on the stack // trace (=> no macro) or b) the attributes have already been set by Python pre-processing. if (env == null) { return new AttributesAndLocation(args, location); } boolean hasName = args.containsAttributeNamed("generator_name"); boolean hasFunc = args.containsAttributeNamed("generator_function"); // TODO(bazel-team): resolve cases in our code where hasName && !hasFunc, or hasFunc && !hasName if (hasName || hasFunc) { return new AttributesAndLocation(args, location); } Pair<FuncallExpression, BaseFunction> topCall = env.getTopCall(); if (topCall == null || !(topCall.second instanceof UserDefinedFunction)) { return new AttributesAndLocation(args, location); } FuncallExpression generator = topCall.first; BaseFunction function = topCall.second; String name = generator.getNameArg(); ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder(); for (String attributeName : args.getAttributeNames()) { builder.put(attributeName, args.getAttributeValue(attributeName)); } builder.put("generator_name", (name == null) ? args.getAttributeValue("name") : name); builder.put("generator_function", function.getName()); if (generator.getLocation() != null) { location = generator.getLocation(); } String relativePath = maybeGetRelativeLocation(location, label); if (relativePath != null) { builder.put("generator_location", relativePath); } try { return new AttributesAndLocation( new BuildLangTypedAttributeValuesMap(builder.build()), location); } catch (IllegalArgumentException ex) { // We just fall back to the default case and swallow any messages. return new AttributesAndLocation(args, location); } }
/** * Creates a {@link Rule} instance, adds it to the {@link Package.Builder} and returns it. * * @param context the package-building context in which this rule was declared * @param ruleClass the {@link RuleClass} of the rule * @param attributeValues a {@link BuildLangTypedAttributeValuesMap} mapping attribute names to * attribute values of build-language type. Each attribute must be defined for this class of * rule, and have a build-language-typed value which can be converted to the appropriate * native type of the attribute (i.e. via {@link BuildType#selectableConvert}). There must be * a map entry for each non-optional attribute of this class of rule. * @param ast the abstract syntax tree of the rule expression (mandatory because this looks up a * {@link Location} from the {@code ast}) * @param env the lexical environment of the function call which declared this rule (optional) * @throws InvalidRuleException if the rule could not be constructed for any reason (e.g. no * {@code name} attribute is defined) * @throws NameConflictException if the rule's name or output files conflict with others in this * package * @throws InterruptedException if interrupted */ public static Rule createAndAddRule( PackageContext context, RuleClass ruleClass, BuildLangTypedAttributeValuesMap attributeValues, FuncallExpression ast, @Nullable Environment env) throws InvalidRuleException, NameConflictException, InterruptedException { return createAndAddRule( context.pkgBuilder, ruleClass, attributeValues, context.eventHandler, ast, ast.getLocation(), env); }
/** * Collects and returns all the Java objects reachable in Skylark from (and including) * firstClassObject with the corresponding SkylarkSignature annotations. * * <p>Note that the {@link SkylarkSignature} annotation for firstClassObject - firstAnnotation - * is also an input parameter, because some top level Skylark built-in objects and methods are not * annotated on the class, but on a field referencing them. */ void collect( SkylarkModule firstModule, Class<?> firstClass, Map<String, SkylarkModuleDoc> modules) { Set<Class<?>> processedClasses = new HashSet<>(); LinkedList<Class<?>> classesToProcess = new LinkedList<>(); Map<Class<?>, SkylarkModule> annotations = new HashMap<>(); classesToProcess.addLast(firstClass); annotations.put(firstClass, firstModule); while (!classesToProcess.isEmpty()) { Class<?> classObject = classesToProcess.removeFirst(); SkylarkModule annotation = annotations.get(classObject); processedClasses.add(classObject); if (!modules.containsKey(annotation.name())) { modules.put(annotation.name(), new SkylarkModuleDoc(annotation, classObject)); } SkylarkModuleDoc module = modules.get(annotation.name()); if (module.javaMethodsNotCollected()) { ImmutableMap<Method, SkylarkCallable> methods = FuncallExpression.collectSkylarkMethodsWithAnnotation(classObject); ArrayList<SkylarkJavaMethod> methodList = new ArrayList<>(); for (Map.Entry<Method, SkylarkCallable> entry : methods.entrySet()) { methodList.add(new SkylarkJavaMethod(entry.getKey(), entry.getValue())); } module.setJavaMethods(methodList); for (Map.Entry<Method, SkylarkCallable> method : methods.entrySet()) { Class<?> returnClass = method.getKey().getReturnType(); if (returnClass.isAnnotationPresent(SkylarkModule.class) && !processedClasses.contains(returnClass)) { classesToProcess.addLast(returnClass); annotations.put(returnClass, returnClass.getAnnotation(SkylarkModule.class)); } } } } }