import com.github.houbie.lesscss.builder.CompilationUnit
// Tell gradle which plugins we use and where to find them
///////////////////////////////////////////////////////////
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.github.houbie:lesscss-gradle-plugin:1.0.0-less-1.7.0'
classpath 'com.eriwen:gradle-js-plugin:1.8.0'
}
}
apply plugin: 'lesscss'
apply plugin: 'js'
// The dependencies we use (a grails plugin that packages the bootstrap source) and where to find them
///////////////////////////////////////////////////////////////////////////////////////////////////////
repositories {
mavenCentral()
maven { url 'http://repo.grails.org/grails/plugins-releases-local' }
}
configurations {
bootstrap
}
dependencies {
bootstrap 'org.grails.plugins:twitter-bootstrap:3.1.1@zip'
compile 'com.google.gdata:core:1.47.1'
}
//property definitions
///////////////////////
ext.webDir = 'web' //location of css, js, etc.
ext.isDevBuild = true //variable for later use (in real project probably based on project version)
//less compiler configuration
//////////////////////////////
lessc {
destinationDir = "$webDir/css"
sourceDir "$webDir/less", "$buildDir/bootstrap/web-app/less"
include 'bootstrap.less', 'theme.less'
//generate source maps so that Google Chrome shows the less source in the inspector i.s.o. the raw CSS
options.sourceMap = true
//the generated css file contains a reference to the source map, this reference will be relative to
//the sourceMapBasepath in this case it will be in the same directory as the css itself
//(default location of source maps)
options.sourceMapBasepath = file("$webDir/css").absolutePath
//we could try to specify the sourceMapxxx options so that the browser can load the LESS sources
//directly, but this is not trivial since our sources reside in different locations
//therefore we just copy all the less source code into the source map file
options.sourceMapLessInline = true
//we can define/override less variables with globalVars/modifyVars resp.
//modifyVars will always take precedence, no matter what you put in the less source
//this can be useful if f.e. you want to use a different coloring scheme for development builds versus release builds
if (isDevBuild) {
options.modifyVars = ['brand-primary': 'purple']
}
//the preCompile closure gives us the opportunity to change the options and destination files on a per-css-basis
preCompile { FileTreeElement src, CompilationUnit unit ->
if (src.name == 'theme.less') {
//the bootstrap examples expect 'bootstrap-theme.css' i.s.o. the default 'theme.css'
unit.destination = project.file("$webDir/css/bootstrap-theme.css")
}
}
}
//lessc compiler daemon for development use
lesscDaemon {
interval = 200 //scan each 200 milliseconds for changes
//uncomment if you don't want to wait for compilations to finish by using
//the super fast node.js lessc compiler in dev mode
//engine = 'commandline'
//uncomment if lessc is not on the system path; don't forget the '.cmd' extension on windows
//lesscExecutable = '/opt/local/bin/lessc'
}
// install the bootstrap sources so that they can easily be customized
///////////////////////////////////////////////////////////////////////
task init {
dependsOn 'copyBootstrapResources', 'minifyJs', 'createCustomizationFiles', 'createCustomLess', 'createSemanticHtml'
}
task explodeBootstrap(type: Copy) { task ->
//we declared a dependency on bootstrap just like on any other (java) library
//here we unzip that dependency
from(zipTree(configurations.bootstrap.fileCollection { dep -> dep.name == 'twitter-bootstrap' }.singleFile));
into "$buildDir/bootstrap"
}
task copyBootstrapResources(type: Copy, dependsOn: explodeBootstrap) {
from "$buildDir/bootstrap/web-app"
into "$webDir"
include 'js/**/*', 'fonts/**/*'
}
//normaly we should use the combineJs task from the js plugin, but that one does not preserve order
task combineJsFiles(dependsOn: 'explodeBootstrap') << {
def srcDir = file("$project.webDir/js")
def includes = ["bootstrap-transition.js",
"bootstrap-alert.js",
"bootstrap-button.js",
"bootstrap-carousel.js",
"bootstrap-collapse.js",
"bootstrap-dropdown.js",
"bootstrap-modal.js",
"bootstrap-tooltip.js",
"bootstrap-popover.js",
"bootstrap-scrollspy.js",
"bootstrap-tab.js",
"bootstrap-affix.js"]
def output = file("$project.webDir/js/bootstrap.js")
output.createNewFile()
includes.each {
output << file("$project.webDir/js/$it").text
}
}
combineJs {
source = "$project.webDir/js"
include "bootstrap-transition.js",
"bootstrap-alert.js",
"bootstrap-button.js",
"bootstrap-carousel.js",
"bootstrap-collapse.js",
"bootstrap-dropdown.js",
"bootstrap-modal.js",
"bootstrap-tooltip.js",
"bootstrap-popover.js",
"bootstrap-scrollspy.js",
"bootstrap-tab.js",
"bootstrap-affix.js"
dest = file("$project.webDir/js/bootstrap.js")
}
//from js plugin
minifyJs {
source = file "$webDir/js/bootstrap.js"
dest = file("$webDir/js/bootstrap.min.js")
closure {
warningLevel = 'QUIET'
}
}
minifyJs.dependsOn combineJsFiles
task createCustomizationFiles(dependsOn: explodeBootstrap) << {
def lessDir = file("$webDir/less")
lessDir.mkdirs()
//create a file in which we can override the standard bootstrap variables (colors etc.)
new File(lessDir, "custom-variables.less").createNewFile()
//create a file in which we can define custom styles according to our application semantics
//these styles will combine/reuse bootstrap styles, but have meaningfull names based on our application's abstractions
new File(lessDir, "application.less").createNewFile()
//have our custom less files imported into bootstrap.less and theme.less
file("$buildDir/bootstrap/web-app/less/bootstrap.less").text += '''
@import "custom-variables.less";
@import "application.less";'''
file("$buildDir/bootstrap/web-app/less/theme.less").text += '@import "custom-variables.less";'
}
// create example material
// this part will not be present in a real project
///////////////////////////////////////////////////
task createCustomLess(dependsOn: createCustomizationFiles) << {
//override a variable
file("$webDir/less/custom-variables.less").text = '@brand-info: blue;'
//create some example styles with semantic meaning
file("$webDir/less/application.less").text = '''
/* define your own semantics */
.article {
.make-row(); // Mixin provided by Bootstrap
h3 {
color: @brand-info
}
.main-section {
.make-lg-column(5); // Mixin provided by Bootstrap
}
.aside {
.make-md-column(2); // Mixin provided by Bootstrap
}
}
/* or even better with (custom) HTML5 tags */
article {
.make-row();
h3 {
color: @brand-info
}
section.main {
.make-lg-column(5);
}
aside {
.make-md-column(2);
}
}'''
}
//download the bootstrap distribution for the examples
task downloadBootstrap {
def zipFile = file("$buildDir/bootstrap.zip")
outputs.file zipFile
doLast {
mkdir zipFile.parentFile
ant.get(src: 'https://github.com/twbs/bootstrap/archive/v3.1.1.zip', dest: zipFile)
}
}
task copyBootstrapExamples(type: Copy, dependsOn: downloadBootstrap) {
from zipTree(downloadBootstrap.outputs.files.singleFile)
into "$webDir/bootstrap-examples"
include 'bootstrap-3.1.1/docs/examples/**/*'
include 'bootstrap-3.1.1/docs/assets/**/*'
eachFile { details ->
//shorten the paths
details.path = (details.path - 'bootstrap-3.1.1/docs/examples') - 'bootstrap-3.1.1/docs'
}
filter { line ->
//adjust the relative paths to css and javascript
line.replace('../../dist/', '../../')
.replace('bootstrap.min.css', 'bootstrap.css')
.replace('theme.min.css', 'theme.css')
.replace('../../assets', '../assets')
}
}
//clean up empty dirs generated by copyBootstrapExamples
task cleanBootstrapExamples(type: Delete, dependsOn: copyBootstrapExamples) {
delete "$webDir/bootstrap-examples/bootstrap-3.1.1"
}
task createSemanticHtml(type: Copy, dependsOn: cleanBootstrapExamples) {
//copy the starter template example and add some html to it
from "$webDir/bootstrap-examples/starter-template/index.html"
into "$webDir/bootstrap-examples/starter-template/"
eachFile { details ->
details.name = 'semantics.html'
}
filter { line ->
line.replace '', '''
Main section with our own, semanticized, HTML
Lorem ipsum dolor sit amet, magna maiorum corpora ea ius, quas tation oporteat ei eam, fastidii mandamus
efficiantur vim ne. Ius no amet luptatum democritum, ex eam alia feugiat dolorum. Ei veri graece eos, no
sit movet nominati suavitate. Zril partiendo ocurreret his an.
Main section with our own, even better semanticized, HTML5
Lorem ipsum dolor sit amet, magna maiorum corpora ea ius, quas tation oporteat ei eam, fastidii mandamus
efficiantur vim ne. Ius no amet luptatum democritum, ex eam alia feugiat dolorum. Ei veri graece eos, no
sit movet nominati suavitate. Zril partiendo ocurreret his an.