<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2015 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~      http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<chapter id='software_model_extend'>
    <title>Extending the software model</title>
    <note>
        <para>
            Support for the software model is currently <link linkend="feature_lifecycle">incubating</link>.
            Please be aware that the DSL, APIs and other configuration may change in later Gradle versions.
        </para>
    </note>
    <para>
        One of the strengths of Gradle has always been its extensibility, and its adaptability to new domains.
        The software model takes this extensibility to a new level, enabling the deep modeling of specific domains via
        richly typed DSLs.
        The following chapter describes how the model and the corresponding DSLs can be extended to support domains like
        <link linkend="java_software">Java</link>, <link linkend="play_plugin">Play Framework</link> or
        <link linkend="native_software">native software development</link>.
        Before reading this you should be familiar with the Gradle software model
        <link linkend='software_model'>rule based configuration</link> and
        <link linkend='software_model_concepts'>concepts</link>.
    </para>
    <para>
        The following build script is an example of using a custom software model for building Markdown based
        documentation:
    </para>
    <sample id="sm-extensible-build-script" title="an example of using a custom software model"
            dir="customModel/languageType/" includeLocation="true">
        <sourcefile file="build.gradle" snippet="build-script"/>
    </sample>
    <para>
        The rest of this chapter is dedicated to explaining what is going on behind this build script.
    </para>
    <section>
        <title>Concepts</title>
        <para>
            A custom software model type has a public type, a base interface and internal views.
            Multiple such types then collaborate to define a custom software model.
        </para>
        <section>
            <title>Public type and base interfaces</title>
            <para>
                Extended types declare a <emphasis>public type</emphasis> that extends a
                <emphasis>base interface</emphasis>:
            </para>
            <itemizedlist>
                <listitem>
                    Components extend the <apilink class="org.gradle.platform.base.ComponentSpec"/> base interface
                </listitem>
                <listitem>
                    Binaries extend the <apilink class="org.gradle.platform.base.BinarySpec"/> base interface
                </listitem>
                <listitem>
                    Source sets extend the <apilink class="org.gradle.language.base.LanguageSourceSet"/> base interface
                </listitem>
            </itemizedlist>
            <para>
                The <emphasis>public type</emphasis> is exposed to build logic.
            </para>
        </section>
        <section>
            <title>Internal views</title>
            <para>
                Adding internal views to your model type, you can make some data visible to build logic via a public
                type, while hiding the rest of the data behind the internal view types.
                This is covered in a <link linkend='software-model-extend-internal-views'>dedicated section</link> below.
            </para>
        </section>
        <section>
            <title>Components all the way down</title>
            <para>
                Components are composed of other components. A source set is just a special kind of component representing sources. It might be that the sources are provided, or generated. Similarily, some components are composed of different binaries, which are built by tasks. All buildable components are built by tasks. In the software model, you will write rules to generate both binaries from components and tasks from binaries.
            </para>
        </section>
    </section>
    <section>
        <title>Components</title>
        <para>
            To declare a custom component type one must extend
            <apilink class="org.gradle.platform.base.ComponentSpec"/>, or one of the following, depending on the use case:
            <itemizedlist>
                <listitem><apilink class="org.gradle.platform.base.SourceComponentSpec"/> represents a component which has sources</listitem>
                <listitem><apilink class="org.gradle.platform.base.VariantComponentSpec"/> represents a component which generates different binaries based on context (target platforms, build flavors, ...). Such a component generally produces multiple binaries.</listitem>
                <listitem><apilink class="org.gradle.platform.base.GeneralComponentSpec"/> is a convenient base interface for components that are built from sources and variant-aware. This is the typical case for a lot of software components, and therefore it should be in most of the cases the base type to be extended.</listitem>
            </itemizedlist>
        </para>
        <para>
            The core software model includes more types that can be used as base for extension. For example:
            <apilink class="org.gradle.platform.base.LibrarySpec"/> and
            <apilink class="org.gradle.platform.base.ApplicationSpec"/> can also be extended in this manner.
            Theses are no-op extensions of <literal>GeneralComponentSpec</literal> used to describe a software
            model better by distinguishing libraries and applications components.
            <apilink class="org.gradle.testing.base.TestSuiteSpec"/> should be used for all components that describe a test suite.
        </para>
        <sample id="component-declaration" title="Declare a custom component"
                dir="customModel/languageType/buildSrc/src/main/groovy/sample/documentation">
            <sourcefile file="DocumentationComponent.groovy" snippet="component-declaration"/>
        </sample>
        <para>
            Types extending <literal>ComponentSpec</literal> are registered via a rule
            annotated with <apilink class="org.gradle.platform.base.ComponentType"/>:
        </para>
        <sample id="component-registration" title="Register a custom component"
                dir="customModel/languageType/buildSrc/src/main/groovy/sample/documentation">
            <sourcefile file="DocumentationPlugin.groovy" snippet="component-registration"/>
        </sample>
    </section>
    <section>
        <title>Binaries</title>
        <para>
            To declare a custom binary type one must extend
            <apilink class="org.gradle.platform.base.BinarySpec"/>.
        </para>
        <sample id="binary-declaration" title="Declare a custom binary"
                dir="customModel/languageType/buildSrc/src/main/groovy/sample/documentation">
            <sourcefile file="DocumentationBinary.groovy" snippet="binary-declaration"/>
        </sample>
        <para>
            Types extending <literal>BinarySpec</literal> are registered via a rule
            annotated with <apilink class="org.gradle.platform.base.ComponentType"/>:
        </para>
        <sample id="binary-registration" title="Register a custom binary"
                dir="customModel/languageType/buildSrc/src/main/groovy/sample/documentation">
            <sourcefile file="DocumentationPlugin.groovy" snippet="binary-registration"/>
        </sample>
    </section>
    <section>
        <title>Source sets</title>
        <para>
            To declare a custom source set type one must extend
            <apilink class="org.gradle.language.base.LanguageSourceSet"/>.
        </para>
        <sample id="markdown-lang-declaration" title="Declare a custom source set"
                dir="customModel/languageType/buildSrc/src/main/groovy/sample/markdown">
            <sourcefile file="MarkdownSourceSet.groovy" snippet="markdown-lang-declaration"/>
        </sample>
        <para>
            Types extending <literal>LanguageSourceSet</literal> are registered via a rule
            annotated with <apilink class="org.gradle.platform.base.ComponentType"/>:
        </para>
        <sample id="markdown-lang-registration" title="Register a custom source set"
                dir="customModel/languageType/buildSrc/src/main/groovy/sample/markdown">
            <sourcefile file="MarkdownPlugin.groovy" snippet="markdown-lang-registration"/>
        </sample>
        <para>
            Setting the <emphasis>language name</emphasis> is mandatory.
        </para>
    </section>
    <section>
        <title>Putting it all together</title>
        <section>
            <title>Generating binaries from components</title>
            <para>
                Binaries generation from components is done via rules annotated with
                <apilink class="org.gradle.platform.base.ComponentBinaries"/>.
                This rule generates a <literal>DocumentationBinary</literal> named <literal>exploded</literal>
                for each <literal>DocumentationComponent</literal> and sets its <literal>outputDir</literal> property:
            </para>
            <sample id="binaries-gen" title="Generates documentation binaries"
                    dir="customModel/languageType/buildSrc/src/main/groovy/sample/documentation">
                <sourcefile file="DocumentationPlugin.groovy" snippet="binaries-generation"/>
            </sample>
        </section>
        <section>
            <title>Generating tasks from binaries</title>
            <para>
                Tasks generation from binaries is done via rules annotated with
                <apilink class="org.gradle.platform.base.BinaryTasks"/>.
                This rule generates a <literal>Copy</literal> task
                for each <literal>TextSourceSet</literal>
                of each <literal>DocumentationBinary</literal>:
            </para>
            <sample id="text-tasks-gen" title="Generates tasks for text source sets"
                    dir="customModel/languageType/buildSrc/src/main/groovy/sample/documentation">
                <sourcefile file="DocumentationPlugin.groovy" snippet="text-tasks-generation"/>
            </sample>
            <para>
                This rule generates a <literal>MarkdownCompileTask</literal> task
                for each <literal>MarkdownSourceSet</literal>
                of each <literal>DocumentationBinary</literal>:
            </para>
            <sample id="markdown-tasks-gen" title="Register a custom source set"
                    dir="customModel/languageType/buildSrc/src/main/groovy/sample/markdown">
                <sourcefile file="MarkdownPlugin.groovy" snippet="markdown-tasks-generation"/>
            </sample>
            <para>
                See the sample source for more on the <literal>MarkdownCompileTask</literal> task.
            </para>
        </section>
        <section>
            <title>Using your custom model</title>
            <para>
                This build script demonstrate usage of the custom model defined in the sections above:
            </para>
            <sample id="sm-extensible-build-script-re" title="an example of using a custom software model"
                    dir="customModel/languageType/" includeLocation="true">
                <sourcefile file="build.gradle" snippet="build-script"/>
            </sample>
            <para>
                And in the components reports for such a build script we can see our model types properly registered:
            </para>
            <sample id="softwareModelExtend-components" title="foo bar" dir="customModel/languageType/">
                <output args="-q components"/>
            </sample>
        </section>
    </section>
    <section id='software-model-extend-internal-views'>
        <title>About internal views</title>
        <para>
            Internal views can be added to an already registered type or to a new custom type.
            In other words, using internal views, you can attach extra properties to already registered components,
            binaries and source sets types like <literal>JvmLibrarySpec</literal>, <literal>JarBinarySpec</literal>
            or <literal>JavaSourceSet</literal> and to the custom types you write.
        </para>
        <para>
            Let's start with a simple component public type and its internal view declarations:
        </para>
        <sample id="iv-type-declaration" title="public type and internal view declaration"
                dir="customModel/internalViews">
            <sourcefile file="build.gradle" snippet="type-declaration"/>
        </sample>
        <para>
            The type registration is as follows:
        </para>
        <sample id="iv-type-registration" title="type registration" dir="customModel/internalViews">
            <sourcefile file="build.gradle" snippet="type-registration"/>
        </sample>
        <para>
            The <literal>internalView(type)</literal> method of the type builder can be called several times.
            This is how you would add several internal views to a type.
        </para>
        <para>
            Now, let's mutate both public and internal data using some rule:
        </para>
        <sample id="iv-view-mutation" title="public and internal data mutation" dir="customModel/internalViews">
            <sourcefile file="build.gradle" snippet="internal-view-mutation"/>
        </sample>
        <para>
            Our <literal>internalData</literal> property should not be exposed to build logic.
            Let's check this using the <literal>model</literal> task on the following build file:
        </para>
        <sample id="softwareModelExtend-iv-model" title="example build script and model report output"
                dir="customModel/internalViews">
            <sourcefile file="build.gradle" snippet="build-script"/>
            <output args="-q model"/>
        </sample>
        <para>
            We can see in this report that <literal>publicData</literal> is present and that
            <literal>internalData</literal> is not.
        </para>
    </section>
</chapter>
