The Gradle User Guide often mentions that Gradle is declarative and uses build-by-convention. What does this mean?
From what I understand it means that, for example, in java plugin there are conventions like
source must be in src/main/java
,tests must be in src/main/test
, resources in src/main/resources
, ready jars in build/libs
and so on. However, Gradle does not oblige you to use these conventions and you can change them if you want.
But with the first concept, I have a bigger problem with understanding. Like SQL you say what you want to do with your queries but do not say how the Database System will get them, which algorithm to use to extract the data etc.
Please, tell me more to understand these concepts properly. Thanks.
Your understanding of build by convention is correct, so I don't have to add anything there. (Also see Jeff's answer.)
The idea behind declarative is that you don't have to work on the task level, implementing/declaring/configuring all tasks and their dependencies yourself, but can work on a higher, more declarative level. You just say "this is a Java project" (apply plugin: "java"
), "here is my binary repository" (repositories { ... }
), "here are my sources" (sourceSets { ... }
), "these are my dependencies" (dependencies { ... }
). Based on this declarative information, Gradle will then figure out which tasks are required, what their dependencies are, and how they need to be configured.
In order to understand a declarative style of programming it is useful to compare and contrast it against an imperative programming style.
Declarative Programming allows us to specify what we want to get done.
In Imperative Programming we specify how we get something done.
So when we use gradle,as Peter describes, we make declarations, declaration such as, "This is a Java Project" or "This is a Java Web Application"
Gradle then, makes use of plugins that offer the service of handling the building of things like "Java Projects" or "Web Applications". This is nice because it is the Gradle Plugin that contains the implementation details that concerns itself with such tasks as compiling java classes and building war files.
Contrast this against another build system, Make, which is more imperative in nature. Lets take a look at a simple Make rule from taken from here:
foo.o : foo.c defs.h
cc -c -g foo.c
So here, we see a rule that describes how to build an object file foo.o from a C source file and a C header file.
The Make rule does two things.
The first line says that a foo.o file depends on a foo.c and foo.h. This line is kind of declarative in so far as Make knows how to check the timestamp on the file foo.o to see if it is older than the files foo.c and foo.h. and if foo.o is older then Make will invoke the command that follows on the next line.
The next line is the imperative one.
The second line specifies exactly what command to run (cc - a C compiler) when a foo.o file is older than either of the files foo.c or foo.h. Note also that the person who is writing the Makefile rule must know what flags that are passed to the cc command.
Build by convention is the idea that if you follow the default conventions, then your builds will be much simpler. So while you can change the source directory, you don't need to explicitly specify the source directory. Gradle comes with logical defaults. This is also called convention over configuration.
This part edited to be more clear about declarative nature based on Peter's answer:
The idea of the build being declarative is that you don't need to specify every step that needs to be done. You don't say "do step 1, do step 2, etc". You define the plugins (or tasks) that need to be applied and gradle then builds a task execution graph and figures out what order to execute things in.