如何在gradle插件中添加新的语言源目录?

时间:2017-02-04 12:47:32

标签: gradle gradle-plugin

我想为任意语言foo提供以下工作gradle脚本:

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"
        }
    }
}

我还想在foosrc/main/foo拥有标准的src/test/foo源目录。

如何编写gradle插件来实现此类功能?是否有可能这样做?

我为下面发布的需求提供了“类似解决方案”的解决方法,但仍希望了解添加新语言源目录的正确方法。

2 个答案:

答案 0 :(得分:3)

要实现这样的功能,应该扩展现有的源集(或至少maintest源集。它看起来像maintest来源集are defined in 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]

答案 1 :(得分:3)

你的用例的源代码会很长,但是这里有几个指针可以帮助你。

看看Scala Plugin。它完全符合您的需求(因为它看起来像是遵循源代码的java约定),还有更多。如果您正在尝试编写任何插件,我建议您查看整个gradle源代码。

您希望看到的确切位置是:

  1. ScalaBasePlugin#configureSourceSetDefaults - 这是顶级配置发生的地方
  2. DefaultScalaSourceSet - 实际的源集类
  3. 在您的情况下,您可能只想将所有scala字符串重命名为foo,并删除您不需要的所有配置(例如实际编译)。

    该插件说明了如何添加默认源目录和方法:

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

    负责添加生成的源。基本上它只需要默认的源目录工厂并使用它。您可以使用注入添加所有默认的gradle工厂(请参阅Scala插件,只需使用javax.inject.Inject工程)。

    您还可以查看groovy插件。请注意,例如DefaultGroovySourceSet看起来像是scala插件中的那个。