Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How Gradle Plugin add method to a specific block?

I wonder how Gradle achieve this mechanism : add a specific method to a script block that only work in that block.

For example, If I want to use implementation method, I have to apply plugin: 'java', But I can only use implementation in dependencies{}

like image 806
ratsafalig Avatar asked Mar 04 '26 15:03

ratsafalig


1 Answers

dependencies block is just a DependencyHandler. The default implementation is DefaultDependencyHandler. Note, that it implements MethodMixIn:

A decorated domain object type may optionally implement this interface to dynamically expose methods in addition to those declared statically on the type. Note that when a type implements this interface, dynamic Groovy dispatch will not be used to discover opaque methods. That is, methods such as methodMissing() will be ignored.

So, this mixin is something like a methodMissing in Groovy. Whenever an unknown method is called on a Groovy object, if it has methodMissing method, it is called. MethodMixIn seem to work similarly. It defines a single method:

MethodAccess getAdditionalMethods();

Let's look how it is implemented in DefaultDependencyHandler?

public MethodAccess getAdditionalMethods() {
    return dynamicMethods;
}

Where dynamicMethods is initialized as:

dynamicMethods = new DynamicAddDependencyMethods(configurationContainer, new DirectDependencyAdder());

Let's check the DynamicAddDependencyMethods. First, it defines a hasMethod, that returns true when there is a configuration with the given name:

public boolean hasMethod(String name, Object... arguments) {
    return arguments.length != 0 && configurationContainer.findByName(name) != null;
}

After applying Java plugin, you configurations will have objects like api, implementation and so on. So, hasMethod("api") will return true, while hasMethod("API")false, unless you have another plugin that contributes a configuration with that name.

Finaly, tryInvokeMethod in invoked whenever a configuration name is used like a function in dependencies block:

public DynamicInvokeResult tryInvokeMethod(String name, Object... arguments) {
    if (arguments.length == 0) {
        return DynamicInvokeResult.notFound();
    }
    Configuration configuration = configurationContainer.findByName(name);
    if (configuration == null) {
        return DynamicInvokeResult.notFound();
    }

    List<?> normalizedArgs = CollectionUtils.flattenCollections(arguments);
    if (normalizedArgs.size() == 2 && normalizedArgs.get(1) instanceof Closure) {
        return DynamicInvokeResult.found(dependencyAdder.add(configuration, normalizedArgs.get(0), (Closure) normalizedArgs.get(1)));
    } else if (normalizedArgs.size() == 1) {
        return DynamicInvokeResult.found(dependencyAdder.add(configuration, normalizedArgs.get(0), null));
    } else {
        for (Object arg : normalizedArgs) {
            dependencyAdder.add(configuration, arg, null);
        }
        return DynamicInvokeResult.found();
    }
}

As you see, it just looks up for a named configuration and adds the dependency to it.

That's how it's made.

like image 74
madhead - StandWithUkraine Avatar answered Mar 07 '26 03:03

madhead - StandWithUkraine