Как добавить новые исходные каталоги языка в плагин Gradle?

Я хочу иметь следующий рабочий скрипт Gradle для произвольного языка foo:

sourceSets {
    main {
        java {
            srcDir "${project.buildDir}/generated-sources/gen-java"
        }
        foo {
            srcDir "${project.buildDir}/generated-sources/gen-foo"
        }
    }
    test {
        java {
            srcDir "${project.buildDir}/generated-sources/gen-test-java"
        }
        foo {
            srcDir "${project.buildDir}/generated-sources/gen-test-foo"
        }
    }
}

Я также хочу иметь стандартные исходные каталоги foo в src/main/foo, src/test/foo.

Как мне написать плагин Gradle для достижения такой функциональности? Есть ли возможность это сделать?

У меня есть обходной путь, похожий на решение, для моих нужд, опубликованный ниже, но я все еще хочу понять, как правильно добавлять новые каталоги исходных языков.


person Sergey Fedorov    schedule 04.02.2017    source источник


Ответы (2)


Для достижения такой функциональности необходимо расширить существующие исходные наборы (или, по крайней мере, main и test исходных наборов). Похоже, что main и test исходные наборы определены в JavaPlugin. Расширение возможно через объект Convention.

public class FooPlugin implements Plugin<ProjectInternal> {
    @Override
    public void apply(ProjectInternal project) {
        project.getPluginManager().apply(JavaPlugin.class);
        FooExtension ext = project.getExtensions().create(
            "foo",
            FooExtension.class,
            project,
            project.getFileResolver()
        );
        SourceSetContainer cont = (SourceSetContainer) project.getProperties().get("sourceSets");
        cont.all((SourceSet ss) -> {
            String name = ss.getName();
            File sources = project.file("src/" + name + "/foo");
            FooSourceSet fss = ext.getSourceSetsContainer().maybeCreate(name);
            SourceDirectorySet sds = fss.getFoo();
            sds.srcDir(sources);
            Convention sourceSetConvention = (Convention) InvokerHelper.getProperty(ss, "convention");
            sourceSetConvention.getPlugins().put("foo", fss);
        });
        project.task("compileFoo");
    }
}

public class FooExtension {

    private final NamedDomainObjectContainer<FooSourceSet> sourceSetsContainer;

    public FooExtension(Project project, FileResolver fileResolver) {
        sourceSetsContainer = project.container(
            FooSourceSet.class,
            new FooSourceSetFactory(fileResolver)
        );
    }

    public NamedDomainObjectContainer<FooSourceSet> getSourceSetsContainer() {
        return sourceSetsContainer;
    }

    public void srcDir(String file) {
        sourceSetsContainer.getByName("main").getFoo().srcDir(file);
    }
}

public class FooSourceSetFactory implements NamedDomainObjectFactory<FooSourceSet> {

    private final FileResolver fileResolver;

    public FooSourceSetFactory(FileResolver fileResolver) {
        this.fileResolver = fileResolver;
    }

    @Override
    public FooSourceSet create(String name) {
        return new DefaultFooSourceSet(name, fileResolver);
    }
}

public interface FooSourceSet {
    public String getName();
    public SourceDirectorySet getFoo();
    public FooSourceSet foo(Closure clsr);
}

public class DefaultFooSourceSet implements FooSourceSet {

    final String name;
    final SourceDirectorySet foo;

    public DefaultFooSourceSet(String displayName, FileResolver fileResolver) {
        this.name = displayName;
        DefaultDirectoryFileTreeFactory ddftf = new DefaultDirectoryFileTreeFactory();
        foo = new DefaultSourceDirectorySet(name, fileResolver, ddftf);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public SourceDirectorySet getFoo() {
        return foo;
    }

    @Override
    public FooSourceSet foo(Closure clsr) {
        ConfigureUtil.configure(clsr, foo);
        return this;
    }
}

public class CompileFooTask extends DefaultTask {

    @TaskAction
    public void compileFoo() {
        SourceSetContainer cont = (SourceSetContainer) getProject().getProperties().get("sourceSets");
        cont.all((SourceSet ss) -> {
            FooSourceSet fss = getProject()
                .getExtensions()
                .getByType(FooExtension.class)
                .getSourceSetsContainer()
                .maybeCreate(ss.getName());
            System.out.println("directories under " + ss.getName()
                + ": " + fss.getFoo().getSrcDirs());
        });
    }
}

Задача compileFoo демонстрирует, что плагин действительно работает. Учитывая фрагмент сценария сборки из вопроса, он печатает такие строки:

directories under main: [<root>/src/main/foo, <root>/build/generated-sources/gen-foo]
directories under test: [<root>/src/test/foo, <root>/build/generated-sources/gen-test-foo]
person Sergey Fedorov    schedule 04.02.2017

Исходный код для вашего варианта использования был бы довольно длинным, но вот пара указателей, которые, надеюсь, вам помогут.

Взгляните на Плагин Scala. Он делает именно то, что вам нужно (похоже, что вы следуете соглашениям Java для источников), а также многое другое. Если вы пытаетесь написать какой-либо плагин, я рекомендую проверить весь исходный код gradle.

Точные места, которые вы хотите увидеть:

  1. ScalaBasePlugin#configureSourceSetDefaults - здесь происходит конфигурация верхнего уровня
  2. DefaultScalaSourceSet - фактический класс исходного набора

В вашем случае вы, вероятно, просто хотите переименовать все строки scala в foo и удалить всю конфигурацию, которая вам не нужна (например, фактическую компиляцию).

Плагин иллюстрирует, как добавить исходные каталоги по умолчанию и метод:

public ScalaSourceSet scala(Closure configureClosure) {
    configure(configureClosure, getScala());
    return this;
}

заботится о добавлении сгенерированных источников. По сути, он просто берет фабрику исходных каталогов по умолчанию и использует ее. Вы можете использовать инъекцию, чтобы добавить все фабрики Gradle по умолчанию (см. Плагин Scala, просто используя javax.inject.Inject работает).

Вы также можете проверить плагин groovy. Обратите внимание, что, например. DefaultGroovySourceSet выглядит так же, как в плагине scala.

person MartinTeeVarga    schedule 20.02.2017