diff --git a/.gitignore b/.gitignore
index 65cca15..07fc93e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
+/src/it/concatenate/src/main/less/less.less
.classpath
.project
.settings/
target/
+.svn
\ No newline at end of file
diff --git a/README.md b/README.md
index 348bda5..3e01b4d 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
-Official LESS CSS Maven Plugin
-==============================
+LESS CSS Maven Plugin - SJV
+===========================
+For official versoin see: https://github.com/marceloverdijk/lesscss-maven-plugin
Usage
-----
@@ -9,7 +10,7 @@ Declare the plugin and its goals. The process-sources phase is bound to by defau
org.lesscss
lesscss-maven-plugin
- 1.3.0
+ 1.3.3-SJV
@@ -28,7 +29,7 @@ Example configuration for web project
org.lesscss
lesscss-maven-plugin
- 1.3.0
+ 1.3.3-SJV
${project.basedir}/src/main/webapp/less
${project.build.directory}/${project.build.finalName}/css
@@ -45,6 +46,37 @@ Example configuration for web project
+
+Exmple configuration with two configurations....
+------------------------------------------------
+
+ org.lesscss
+ lesscss-maven-plugin
+ 1.3.3-SJV
+
+
+
+
+ ${project.basedir}/src/main/less/common
+ ${project.build.directory}/${project.build.finalName}/css/common
+ true
+
+
+ ${project.basedir}/src/main/webapp/less/project
+ ${project.build.directory}/${project.build.finalName}/css/project
+ false
+
+
+
+
+
+
+
+ compile
+
+
+
+
All configuration options
@@ -58,7 +90,11 @@ All configuration options
+ force (boolean) - When true forces the LESS compiler to always compile the LESS sources. By default LESS sources are only compiled when modified (including imports) or the CSS stylesheet does not exists. Default value is: false.
+ includes (String[]) - List of files to include. Specified as fileset patterns which are relative to the source directory. Default value is: { "**\/*.less" }
+ lessJs (String) - The location of the LESS JavasSript file.
-
++ concatenate (boolean) - When true all less-files will be concatenated into a single file before compiling to css
++ watch (boolean) - When true the plugin watches the sourceDirectory and recompiles the included files after they changed. Instead of configuring it in the pom you can use that option at the command line like this "mvn lesscss:compile -Dlesscss.watch=true". Then it doesn't interfere with other maven lifecycle phases and you can just kill the watch process e.g. with crtl-c. Default value is: false.
++ watchInterval (int) - The interval in milliseconds the plugin waits between the check for file changes. Default value is: 1000 ms.
++ configurationItems - a list of configurationItem.
++ configurationItem - a custom class. Holds a separate configuration, eg all of the above.
List sources
------------
@@ -66,21 +102,6 @@ List sources
To list the LESS sources in your project the lesscss:list goal can be used. It lists the LESS sources and it's imports based on sourceDirectory and optionally includes and excludes configuration options.
-Support
--------
-
-Have a question, or found an issue? Just create a issue: https://github.com/marceloverdijk/lesscss-maven-plugin/issues
-
-
-Authors
--------
-
-**Marcel Overdijk**
-
-+ marcel@overdijk.me
-+ http://twitter.com/marceloverdijk
-+ http://github.com/marceloverdijk
-
Copyright and License
---------------------
diff --git a/pom.xml b/pom.xml
index 1292c4a..178cc37 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
org.lesscss
lesscss-maven-plugin
- 1.3.1-SNAPSHOT
+ 1.3.3-SJV-SNAPSHOT
maven-plugin
Official LESS CSS Maven Plugin
Official LESS CSS Maven Plugin
@@ -40,7 +40,7 @@
org.apache.maven.plugin-testing
maven-plugin-testing-harness
- 1.2
+ 1.3
test
@@ -51,7 +51,7 @@
org.lesscss
lesscss
- 1.3.0
+ 1.3.3.SJV-SNAPSHOT
org.mockito
diff --git a/src/it/concatenate/invoker.properties b/src/it/concatenate/invoker.properties
new file mode 100644
index 0000000..9651b16
--- /dev/null
+++ b/src/it/concatenate/invoker.properties
@@ -0,0 +1 @@
+invoker.goals = clean compile
\ No newline at end of file
diff --git a/src/it/concatenate/pom.xml b/src/it/concatenate/pom.xml
new file mode 100644
index 0000000..ef6e736
--- /dev/null
+++ b/src/it/concatenate/pom.xml
@@ -0,0 +1,26 @@
+
+ 4.0.0
+ org.lesscss.it
+ concatenate
+ testing
+
+
+
+ org.lesscss
+ lesscss-maven-plugin
+ @project.version@
+
+ concatenated.less
+
+
+
+
+ compile
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/it/concatenate/src/main/less/test.less b/src/it/concatenate/src/main/less/test.less
new file mode 100644
index 0000000..00ee14c
--- /dev/null
+++ b/src/it/concatenate/src/main/less/test.less
@@ -0,0 +1,8 @@
+
+
+#header {
+ color: @color;
+}
+h2 {
+ color: @color;
+}
\ No newline at end of file
diff --git a/src/it/concatenate/src/main/less/variables.less b/src/it/concatenate/src/main/less/variables.less
new file mode 100644
index 0000000..76d634e
--- /dev/null
+++ b/src/it/concatenate/src/main/less/variables.less
@@ -0,0 +1,2 @@
+@color: #4d926f;
+
diff --git a/src/it/concatenate/verify.groovy b/src/it/concatenate/verify.groovy
new file mode 100644
index 0000000..dcf91bc
--- /dev/null
+++ b/src/it/concatenate/verify.groovy
@@ -0,0 +1,3 @@
+assert new File(basedir, "target/concatenated.css").exists()
+assert !new File(basedir, "target/test.css").exists()
+assert !new File(basedir, "target/variables.css").exists()
diff --git a/src/it/multiple-config/invoker.properties b/src/it/multiple-config/invoker.properties
new file mode 100644
index 0000000..9651b16
--- /dev/null
+++ b/src/it/multiple-config/invoker.properties
@@ -0,0 +1 @@
+invoker.goals = clean compile
\ No newline at end of file
diff --git a/src/it/multiple-config/pom.xml b/src/it/multiple-config/pom.xml
new file mode 100644
index 0000000..87d8ff2
--- /dev/null
+++ b/src/it/multiple-config/pom.xml
@@ -0,0 +1,38 @@
+
+
+ 4.0.0
+ org.lesscss.it
+ basic
+ testing
+
+
+
+
+ org.lesscss
+ lesscss-maven-plugin
+ @project.version@
+
+
+
+ ${project.basedir}/src/main/less1
+ ${project.build.directory}/css1
+
+
+ ${project.basedir}/src/main/less2
+ ${project.build.directory}/css2
+
+
+
+
+
+
+ compile
+
+
+
+
+
+
+
+
diff --git a/src/it/multiple-config/src/main/less1/firstTest.less b/src/it/multiple-config/src/main/less1/firstTest.less
new file mode 100644
index 0000000..49618d9
--- /dev/null
+++ b/src/it/multiple-config/src/main/less1/firstTest.less
@@ -0,0 +1,8 @@
+@color: #4d926f;
+
+#header {
+ color: @color;
+}
+h2 {
+ color: @color;
+}
\ No newline at end of file
diff --git a/src/it/multiple-config/src/main/less2/secondTest.less b/src/it/multiple-config/src/main/less2/secondTest.less
new file mode 100644
index 0000000..49618d9
--- /dev/null
+++ b/src/it/multiple-config/src/main/less2/secondTest.less
@@ -0,0 +1,8 @@
+@color: #4d926f;
+
+#header {
+ color: @color;
+}
+h2 {
+ color: @color;
+}
\ No newline at end of file
diff --git a/src/it/multiple-config/verify.groovy b/src/it/multiple-config/verify.groovy
new file mode 100644
index 0000000..b3e0e9e
--- /dev/null
+++ b/src/it/multiple-config/verify.groovy
@@ -0,0 +1,2 @@
+assert new File(basedir, "target/css1/firstTest.css").exists()
+assert new File(basedir, "target/css2/secondTest.css").exists()
\ No newline at end of file
diff --git a/src/main/java/org/lesscss/mojo/AbstractLessCssMojo.java b/src/main/java/org/lesscss/mojo/AbstractLessCssMojo.java
index 420d82b..20ee803 100644
--- a/src/main/java/org/lesscss/mojo/AbstractLessCssMojo.java
+++ b/src/main/java/org/lesscss/mojo/AbstractLessCssMojo.java
@@ -15,6 +15,7 @@
package org.lesscss.mojo;
import java.io.File;
+import java.util.List;
import org.apache.maven.plugin.AbstractMojo;
import org.codehaus.plexus.util.Scanner;
@@ -30,6 +31,13 @@ public abstract class AbstractLessCssMojo extends AbstractMojo {
/** @component */
protected BuildContext buildContext;
+ /**
+ * Using configurationItems allows you to have several different compilations on a single project
+ *
+ * @parameter
+ */
+ protected List configurationItems;
+
/**
* The source directory containing the LESS sources.
*
@@ -43,7 +51,7 @@ public abstract class AbstractLessCssMojo extends AbstractMojo {
*
* @parameter
*/
- protected String[] includes = new String[] { "**/*.less" };
+ protected String[] includes = new String[] { INCLUDES_DEFAULT_VALUE };
/**
* List of files to exclude. Specified as fileset patterns which are relative to the source directory.
@@ -52,16 +60,25 @@ public abstract class AbstractLessCssMojo extends AbstractMojo {
*/
protected String[] excludes = new String[] {};
+ static final String INCLUDES_DEFAULT_VALUE = "**/*.less";
+
/**
* Scans for the LESS sources that should be compiled.
*
* @return The list of LESS sources.
*/
- protected String[] getIncludedFiles() {
- Scanner scanner = buildContext.newScanner(sourceDirectory, true);
- scanner.setIncludes(includes);
- scanner.setExcludes(excludes);
+ protected String[] getIncludedFiles(ConfigurationItem configurationItem) {
+ Scanner scanner = buildContext.newScanner(configurationItem.getSourceDirectory(), true);
+ scanner.setIncludes(configurationItem.getIncludes());
+ scanner.setExcludes(configurationItem.getExcludes());
scanner.scan();
return scanner.getIncludedFiles();
}
+
+ /**
+ * Get configuration
+ *
+ * @return a list of ConfigurationItems read from the plugin-configuration
+ */
+ protected abstract List getConfiguration();
}
diff --git a/src/main/java/org/lesscss/mojo/CompileMojo.java b/src/main/java/org/lesscss/mojo/CompileMojo.java
index 9f8d495..f984070 100644
--- a/src/main/java/org/lesscss/mojo/CompileMojo.java
+++ b/src/main/java/org/lesscss/mojo/CompileMojo.java
@@ -17,8 +17,11 @@
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+import org.apache.commons.io.FileUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.codehaus.plexus.util.StringUtils;
import org.lesscss.LessCompiler;
@@ -35,35 +38,64 @@
*/
public class CompileMojo extends AbstractLessCssMojo {
+ private static final int WATCH_INTERVAL_DEFAULT = 1000;
+
/**
* The directory for compiled CSS stylesheets.
*
- * @parameter expression="${lesscss.outputDirectory}" default-value="${project.build.directory}"
+ * @parameter expression="${lesscss.outputDirectory}"
+ * default-value="${project.build.directory}"
* @required
*/
- private File outputDirectory;
+ protected File outputDirectory;
/**
- * When true
the LESS compiler will compress the CSS stylesheets.
+ * When true
the LESS compiler will compress the CSS
+ * stylesheets.
*
* @parameter expression="${lesscss.compress}" default-value="false"
*/
private boolean compress;
/**
- * The character encoding the LESS compiler will use for writing the CSS stylesheets.
+ * When true
the plugin will watch for changes in LESS files and compile if it detects one.
+ *
+ * @parameter expression="${lesscss.watch}" default-value="false"
+ */
+ protected boolean watch = false;
+
+ /**
+ * When true
the plugin will watch for changes in LESS files and compile if it detects one.
+ *
+ * @parameter expression="${lesscss.watchInterval}" default-value="1000"
+ */
+ private int watchInterval = WATCH_INTERVAL_DEFAULT;
+
+ /**
+ * The character encoding the LESS compiler will use for writing the CSS
+ * stylesheets.
*
- * @parameter expression="${lesscss.encoding}" default-value="${project.build.sourceEncoding}"
+ * @parameter expression="${lesscss.encoding}"
+ * default-value="${project.build.sourceEncoding}"
*/
private String encoding;
/**
- * When true
forces the LESS compiler to always compile the LESS sources. By default LESS sources are only compiled when modified (including imports) or the CSS stylesheet does not exists.
+ * When true
forces the LESS compiler to always compile the
+ * LESS sources. By default LESS sources are only compiled when modified
+ * (including imports) or the CSS stylesheet does not exists.
*
* @parameter expression="${lesscss.force}" default-value="false"
*/
private boolean force;
+ /**
+ * If set concatenates the less files and outputs to the filename given
+ *
+ * @parameter expression="${lesscss.concatenateTo}"
+ */
+ private String concatenateTo;
+
/**
* The location of the LESS JavasSript file.
*
@@ -81,73 +113,230 @@ public void execute() throws MojoExecutionException {
long start = System.currentTimeMillis();
- if (getLog().isDebugEnabled()) {
- getLog().debug("sourceDirectory = " + sourceDirectory);
- getLog().debug("outputDirectory = " + outputDirectory);
- getLog().debug("includes = " + Arrays.toString(includes));
- getLog().debug("excludes = " + Arrays.toString(excludes));
- getLog().debug("force = " + force);
- getLog().debug("lessJs = " + lessJs);
- }
+ List configurationItems = getConfiguration();
- String[] files = getIncludedFiles();
+ if (configurationItems.size() == 0) {
+ return;
+ }
- if (files == null || files.length < 1) {
- getLog().info("Nothing to compile - no LESS sources found");
- } else {
+ for (ConfigurationItem item : configurationItems) {
if (getLog().isDebugEnabled()) {
- getLog().debug("included files = " + Arrays.toString(files));
+ getLog().debug("sourceDirectory = " + item.getSourceDirectory());
+ getLog().debug("outputDirectory = " + item.getOutputDirectory());
+ getLog().debug("includes = " + Arrays.toString(item.getIncludes()));
+ getLog().debug("excludes = " + Arrays.toString(item.getExcludes()));
+ getLog().debug("force = " + item.isForce());
+ getLog().debug("lessJs = " + item.getLessJs());
+ getLog().debug("concatenate = " + item.getConcateanteTo());
+ getLog().debug("watch = " + item.isWatch());
+ getLog().debug("watchInterval = " + item.getWatchInterval());
+ getLog().debug("compress = " + item.isCompress());
}
- LessCompiler lessCompiler = new LessCompiler();
- lessCompiler.setCompress(compress);
- lessCompiler.setEncoding(encoding);
+ String[] files = getIncludedFiles(item);
- if (lessJs != null) {
+ if (item.getConcateanteTo() != null) {
try {
- lessCompiler.setLessJs(lessJs.toURI().toURL());
- } catch (MalformedURLException e) {
- throw new MojoExecutionException(
- "Error while loading LESS JavaScript: " + lessJs.getAbsolutePath(), e);
+
+ String tmpPath = item.getConcateanteTo();
+ File tmpFile = new File(item.getSourceDirectory(), tmpPath);
+
+ boolean updated = isFilesUpdated(item, files, tmpFile);
+ if(!updated){
+ getLog().info("No files updated since last build");
+ return;
+ }
+
+ deleteTemporaryFile(tmpFile);
+ buildConcatenatedFile(item, files, tmpFile);
+ files = setTemporaryAsFileToCompile(tmpPath);
+ } catch (IOException ioe) {
+ getLog().error("Error concatenating files", ioe);
}
}
- for (String file : files) {
- File input = new File(sourceDirectory, file);
-
- buildContext.removeMessages(input);
-
- File output = new File(outputDirectory, file.replace(".less", ".css"));
-
- if (!output.getParentFile().exists() && !output.getParentFile().mkdirs()) {
- throw new MojoExecutionException("Cannot create output directory " + output.getParentFile());
+ if (files == null || files.length < 1) {
+ getLog().info("Nothing to compile - no LESS sources found");
+ } else {
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("included files = " + Arrays.toString(files));
}
- try {
- LessSource lessSource = new LessSource(input);
+ LessCompiler lessCompiler = new LessCompiler();
+ lessCompiler.setCompress(item.isCompress());
+ lessCompiler.setEncoding(item.getEncoding());
- if (output.lastModified() < lessSource.getLastModifiedIncludingImports()) {
- getLog().info("Compiling LESS source: " + file + "...");
- lessCompiler.compile(lessSource, output, force);
- buildContext.refresh(output);
+ if (item.getLessJs() != null) {
+ try {
+ lessCompiler.setLessJs(item.getLessJs().toURI().toURL());
+ } catch (MalformedURLException e) {
+ throw new MojoExecutionException("Error while loading LESS JavaScript: "
+ + item.getLessJs().getAbsolutePath(), e);
}
- else {
- getLog().info("Bypassing LESS source: " + file + " (not modified)");
+ }
+ if (item.isWatch()) {
+ getLog().info("Watching " + item.getOutputDirectory());
+ if (item.isForce()) {
+ force = false;
+ getLog().info("Disabled the 'force' flag in watch mode.");
}
- } catch (IOException e) {
- buildContext.addMessage(input, 0, 0, "Error compiling LESS source", BuildContext.SEVERITY_ERROR, e);
- throw new MojoExecutionException("Error while compiling LESS source: " + file, e);
- } catch (LessException e) {
- String message = e.getMessage();
- if (StringUtils.isEmpty(message)) {
- message = "Error compiling LESS source";
+ Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
+ while (item.isWatch() && !Thread.currentThread().isInterrupted()) {
+ compileIfChanged(files, lessCompiler, item);
+ try {
+ Thread.sleep(item.getWatchInterval());
+ } catch (InterruptedException e) {
+ System.out.println("interrupted");
+ }
}
- buildContext.addMessage(input, 0, 0, "Error compiling LESS source", BuildContext.SEVERITY_ERROR, e);
- throw new MojoExecutionException("Error while compiling LESS source: " + file, e);
+ } else {
+ compileIfChanged(files, lessCompiler, item);
+ }
+
+ getLog().info(
+ "Complete Less compile job finished in "
+ + (System.currentTimeMillis() - start) + " ms");
+ }
+ }
+ }
+
+ private String[] setTemporaryAsFileToCompile(String tmpPath) {
+ String[] files;
+ files = new String[] { tmpPath };
+ return files;
+ }
+
+ private void buildConcatenatedFile(ConfigurationItem item, String[] files, File tmpFile)
+ throws IOException {
+ for (String path : files) {
+ File original = new File(item.getSourceDirectory(), path);
+ System.out.println(original.getAbsolutePath());
+ String content = FileUtils.readFileToString(original);
+ FileUtils.write(tmpFile, content, true);
+ }
+ }
+
+ private void deleteTemporaryFile(File tmpFile) {
+ tmpFile.delete();
+ }
+
+ private boolean isFilesUpdated(ConfigurationItem item, String[] files, File tmpFile) {
+ boolean updated = false;
+ for(String path: files){
+ File original = new File(item.getSourceDirectory(), path);
+ if(original.lastModified() > tmpFile.lastModified()){
+ updated = true;
+ }
+ }
+ return updated;
+ }
+
+ private void compileIfChanged(String[] files, LessCompiler lessCompiler,
+ ConfigurationItem item) throws MojoExecutionException {
+ for (String file : files) {
+ File input = new File(item.getSourceDirectory(), file);
+
+ buildContext.removeMessages(input);
+
+ File output = new File(item.getOutputDirectory(), file.replace(".less", ".css"));
+
+ if (!output.getParentFile().exists() && !output.getParentFile().mkdirs()) {
+ throw new MojoExecutionException("Cannot create output directory "
+ + output.getParentFile());
+ }
+
+ try {
+ LessSource lessSource = new LessSource(input);
+ if (output.lastModified() < lessSource.getLastModifiedIncludingImports()) {
+ long compilationStarted = System.currentTimeMillis();
+ getLog().info("Compiling LESS source: " + file + "...");
+ lessCompiler.compile(lessSource, output, force);
+ buildContext.refresh(output);
+ getLog().info(
+ "Finished compilation to " + outputDirectory + " in "
+ + (System.currentTimeMillis() - compilationStarted) + " ms");
+ } else if (!watch) {
+ getLog().info("Bypassing LESS source: " + file + " (not modified)");
+ }
+ } catch (IOException e) {
+ buildContext.addMessage(input, 0, 0, "Error compiling LESS source",
+ BuildContext.SEVERITY_ERROR, e);
+ throw new MojoExecutionException("Error while compiling LESS source: " + file, e);
+ } catch (LessException e) {
+ String message = e.getMessage();
+ if (StringUtils.isEmpty(message)) {
+ message = "Error compiling LESS source";
}
+ buildContext.addMessage(input, 0, 0, "Error compiling LESS source",
+ BuildContext.SEVERITY_ERROR, e);
+ throw new MojoExecutionException("Error while compiling LESS source: " + file, e);
}
+ }
+ }
- getLog().info("Compilation finished in " + (System.currentTimeMillis() - start) + " ms");
+
+ public List getConfiguration() {
+ if (this.configurationItems == null) {
+ configurationItems = new ArrayList();
+ }else{
+ //TODO: Should not need to do this but somehow an extra configuration
+ //TODO: is included...
+ return configurationItems;
}
+
+
+ ConfigurationItem configurationItem = new ConfigurationItem();
+ boolean configured = false;
+ if (sourceDirectory != null) {
+ configurationItem.setSourceDirectory(sourceDirectory);
+ configured = true;
+ }
+ if (outputDirectory != null) {
+ configurationItem.setOutputDirectory(outputDirectory);
+ configured = true;
+ }
+ if (excludes.length > 0) {
+ configurationItem.setExcludes(excludes);
+ configured = true;
+ }
+ if (includes[0] != INCLUDES_DEFAULT_VALUE) {
+ configurationItem.setIncludes(includes);
+ configured = true;
+ }
+ if (compress) {
+ configurationItem.setCompress(compress);
+ configured = true;
+ }
+ if (watch) {
+ configurationItem.setWatch(watch);
+ if (watchInterval != WATCH_INTERVAL_DEFAULT) {
+ configurationItem.setWatchInterval(watchInterval);
+ }else{
+ configurationItem.setWatchInterval(WATCH_INTERVAL_DEFAULT);
+ }
+ configured = true;
+ }
+ if (encoding != null) {
+ configurationItem.setEncoding(encoding);
+ configured = true;
+ }
+ if (force) {
+ configurationItem.setForce(force);
+ configured = true;
+ }
+ if (lessJs != null) {
+ configurationItem.setLessJs(lessJs);
+ configured = true;
+ }
+ if (concatenateTo != null) {
+ configurationItem.setConcatenateTo(concatenateTo);
+ configured = true;
+ }
+
+ if (configured) {
+ configurationItems.add(configurationItem);
+ }
+
+ return configurationItems;
}
}
diff --git a/src/main/java/org/lesscss/mojo/ConfigurationItem.java b/src/main/java/org/lesscss/mojo/ConfigurationItem.java
new file mode 100644
index 0000000..093f6c7
--- /dev/null
+++ b/src/main/java/org/lesscss/mojo/ConfigurationItem.java
@@ -0,0 +1,112 @@
+package
+org.lesscss.mojo;
+
+import java.io.File;
+
+/**
+ * A configurationItem is a complete set of configuration-parameters
+ * needed for the LessCss to compile a set of .less-files to .css.
+ *
+ * @author Roland Heimdahl
+ *
+ */
+public class ConfigurationItem {
+ private File sourceDirectory;
+ private File outputDirectory;
+ private String[] includes;
+ private String[] excludes;
+ private boolean compress;
+ private boolean watch;
+ private int watchInterval;
+ private String encoding;
+ private boolean force;
+ private File lessJs;
+ private String concatenateTo;
+
+ public File getSourceDirectory() {
+ return sourceDirectory;
+ }
+
+ public File getOutputDirectory() {
+ return outputDirectory;
+ }
+
+ public String[] getIncludes() {
+ return includes;
+ }
+
+ public String[] getExcludes() {
+ return excludes;
+ }
+
+ public void setSourceDirectory(final File sourceDirectory) {
+ this.sourceDirectory = sourceDirectory;
+ }
+
+ public void setOutputDirectory(final File outputDirectory){
+ this.outputDirectory = outputDirectory;
+ }
+
+ public void setIncludes(final String[] includes){
+ this.includes = includes;
+ }
+
+ public void setExcludes(final String[] excludes){
+ this.excludes = excludes;
+ }
+
+ public boolean isCompress() {
+ return compress;
+ }
+
+ public void setCompress(boolean compress) {
+ this.compress = compress;
+ }
+
+ public boolean isWatch() {
+ return watch;
+ }
+
+ public void setWatch(boolean watch) {
+ this.watch = watch;
+ }
+
+ public int getWatchInterval() {
+ return watchInterval;
+ }
+
+ public void setWatchInterval(int watchInterval) {
+ this.watchInterval = watchInterval;
+ }
+
+ public String getEncoding() {
+ return encoding;
+ }
+
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ public boolean isForce() {
+ return force;
+ }
+
+ public void setForce(boolean force) {
+ this.force = force;
+ }
+
+ public File getLessJs() {
+ return lessJs;
+ }
+
+ public void setLessJs(File lessJs) {
+ this.lessJs = lessJs;
+ }
+
+ public String getConcateanteTo() {
+ return concatenateTo;
+ }
+ public void setConcatenateTo(final String concatenate){
+ this.concatenateTo = concatenate;
+ }
+}
diff --git a/src/main/java/org/lesscss/mojo/ListMojo.java b/src/main/java/org/lesscss/mojo/ListMojo.java
index e51556e..d61b391 100644
--- a/src/main/java/org/lesscss/mojo/ListMojo.java
+++ b/src/main/java/org/lesscss/mojo/ListMojo.java
@@ -17,8 +17,10 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
+import java.util.List;
import java.util.Map.Entry;
import org.apache.maven.plugin.MojoExecutionException;
@@ -44,8 +46,15 @@ public void execute() throws MojoExecutionException {
getLog().debug("includes = " + Arrays.toString(includes));
getLog().debug("excludes = " + Arrays.toString(excludes));
}
+
+ List configurationItems = getConfiguration();
- String[] files = getIncludedFiles();
+ if (configurationItems.size() == 0) {
+ return;
+ }
+ ConfigurationItem item = configurationItems.get(0);
+
+ String[] files = getIncludedFiles(item);
if (files == null || files.length < 1) {
getLog().info("No LESS sources found");
@@ -58,9 +67,11 @@ public void execute() throws MojoExecutionException {
LessSource lessSource = new LessSource(lessFile);
listLessSource(lessSource, file, 0, false);
} catch (FileNotFoundException e) {
- throw new MojoExecutionException("Error while loading LESS source: " + lessFile.getAbsolutePath(), e);
+ throw new MojoExecutionException("Error while loading LESS source: "
+ + lessFile.getAbsolutePath(), e);
} catch (IOException e) {
- throw new MojoExecutionException("Error while loading LESS source: " + lessFile.getAbsolutePath(), e);
+ throw new MojoExecutionException("Error while loading LESS source: "
+ + lessFile.getAbsolutePath(), e);
}
}
}
@@ -88,4 +99,32 @@ private void listLessSource(LessSource lessSource, String path, int level, boole
listLessSource(entry.getValue(), entry.getKey(), level + 1, !it.hasNext());
}
}
+
+ public List getConfiguration() {
+ if(this.configurationItems == null){
+ configurationItems = new ArrayList();
+ }
+
+ ConfigurationItem configurationItem = new ConfigurationItem();
+ boolean configured = false;
+ if (sourceDirectory != null) {
+ configurationItem.setSourceDirectory(sourceDirectory);
+ configured = true;
+ }
+
+ if(excludes.length > 0){
+ configurationItem.setExcludes(excludes);
+ configured = true;
+ }
+ if(includes[0] != INCLUDES_DEFAULT_VALUE){
+ configurationItem.setIncludes(includes);
+ configured = true;
+ }
+
+ if (configured) {
+ configurationItems.add(configurationItem);
+ }
+
+ return configurationItems;
+ }
}
diff --git a/src/test/java/org/lesscss/mojo/AbstractLessCssMojoTest.java b/src/test/java/org/lesscss/mojo/AbstractLessCssMojoTest.java
index 7f0fde5..6b52858 100644
--- a/src/test/java/org/lesscss/mojo/AbstractLessCssMojoTest.java
+++ b/src/test/java/org/lesscss/mojo/AbstractLessCssMojoTest.java
@@ -20,6 +20,7 @@
import static org.powermock.api.mockito.PowerMockito.when;
import java.io.File;
+import java.util.List;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.testing.AbstractMojoTestCase;
@@ -59,6 +60,11 @@ public void setUp() throws Exception {
mojo = new AbstractLessCssMojo() {
public void execute() throws MojoExecutionException {
}
+
+ @Override
+ protected List getConfiguration() {
+ return null;
+ }
};
setVariableValueToObject(mojo, "buildContext", buildContext);
@@ -67,19 +73,6 @@ public void execute() throws MojoExecutionException {
setVariableValueToObject(mojo, "excludes", excludes);
}
- @Test
- public void testGetFiles() throws Exception {
- when(buildContext.newScanner(sourceDirectory, true)).thenReturn(scanner);
- when(scanner.getIncludedFiles()).thenReturn(files);
-
- assertSame(files, mojo.getIncludedFiles());
-
- verify(buildContext).newScanner(same(sourceDirectory), eq(true));
- verify(scanner).setIncludes(same(includes));
- verify(scanner).setExcludes(same(excludes));
- verify(scanner).scan();
- }
-
@After
public void tearDown() {
}
diff --git a/src/test/java/org/lesscss/mojo/CompileMojoTest.java b/src/test/java/org/lesscss/mojo/CompileMojoTest.java
index 37118e5..dd47b15 100644
--- a/src/test/java/org/lesscss/mojo/CompileMojoTest.java
+++ b/src/test/java/org/lesscss/mojo/CompileMojoTest.java
@@ -30,6 +30,7 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
+import java.util.List;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
@@ -472,6 +473,20 @@ public void testExecutionMakeDirsFailsWhenOutputDirectoryDoesNotExists() throws
verify(parent).exists();
verify(parent).mkdirs();
}
+
+ @Test
+ public void testGetFiles() throws Exception {
+ when(buildContext.newScanner(sourceDirectory, true)).thenReturn(scanner);
+ when(scanner.getIncludedFiles()).thenReturn(files);
+
+ List items = mojo.getConfiguration();
+ assertSame(files, mojo.getIncludedFiles(items.get(0)));
+
+ verify(buildContext).newScanner(same(sourceDirectory), eq(true));
+ verify(scanner).setIncludes(same(includes));
+ verify(scanner).setExcludes(same(excludes));
+ verify(scanner).scan();
+ }
@After
public void tearDown() {
diff --git a/src/test/java/org/lesscss/mojo/CompileOnChangeMojoTest.java b/src/test/java/org/lesscss/mojo/CompileOnChangeMojoTest.java
new file mode 100644
index 0000000..7048967
--- /dev/null
+++ b/src/test/java/org/lesscss/mojo/CompileOnChangeMojoTest.java
@@ -0,0 +1,110 @@
+package org.lesscss.mojo;
+
+import java.io.File;
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.testing.AbstractMojoTestCase;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+* Tests the watch mode of the CompileMojo.
+*/
+public class CompileOnChangeMojoTest extends AbstractMojoTestCase {
+ @Rule
+ public TemporaryFolder tempFolder = new TemporaryFolder();
+
+ /** {@inheritDoc} */
+ protected void setUp() throws Exception {
+ // required
+ super.setUp();
+ }
+
+ /** {@inheritDoc} */
+ protected void tearDown() throws Exception {
+ // required
+ super.tearDown();
+ }
+
+ /**
+ * @throws Exception
+ * This test touches two files and checks if the bootstrap.css
+ * file has been recompiled after every touch.
+ */
+ @Test
+ public void testIfFileChangeCausesRecompilation() throws Exception {
+ File pom = getTestFile("src/test/resources/change-pom.xml");
+ assertNotNull(pom);
+ assertTrue(pom.exists());
+ final CompileMojo compileMojo = (CompileMojo) lookupMojo("compile", pom);
+ assertNotNull(compileMojo);
+
+ File generatedFile = new File(compileMojo.outputDirectory, "bootstrap.css");
+ long oldLastModified = generatedFile.lastModified();
+
+ compileMojo.outputDirectory = tempFolder.newFolder();
+
+ /*
+ * Run the watching compileMojo in a new thread. This way we can test
+ * recompilation and stop the watching mode asynchronous in this thread.
+ */
+ Thread watchThread = new Thread() {
+ public void run() {
+ try {
+ compileMojo.execute();
+ } catch (MojoExecutionException e) {
+ assertTrue(e.getLongMessage(), true);
+ }
+ }
+ };
+ watchThread.start();
+
+ touchFile(compileMojo, "bootstrap.less");
+ long newLastModified = checkIfFileHasBeenChanged(compileMojo, oldLastModified);
+
+ touchFile(compileMojo, "1/reset.less");
+ newLastModified = checkIfFileHasBeenChanged(compileMojo, newLastModified);
+
+ touchFile(compileMojo, "2/21/variables.less");
+ checkIfFileHasBeenChanged(compileMojo, newLastModified);
+ watchThread.interrupt();
+ }
+
+ private long checkIfFileHasBeenChanged(final CompileMojo compileMojo, long lastModified)
+ throws InterruptedException {
+ long newLastModified = lastModified;
+ long oldLastModified = lastModified;
+
+ File generatedFile = null;
+ long startMillies = System.currentTimeMillis();
+ long currentMillies = startMillies;
+ // check for a maximum of ten seconds if the file has been changed
+ while (oldLastModified >= newLastModified && ((currentMillies - startMillies) < 10000)) {
+ Thread.sleep(200); // wait for 0.2 seconds
+ generatedFile = new File(compileMojo.outputDirectory, "bootstrap.css");
+ newLastModified = generatedFile.lastModified();
+ currentMillies = System.currentTimeMillis();
+ }
+ assertTrue("No recompilation has been done within " + (currentMillies - startMillies)
+ + " ms.", newLastModified > oldLastModified);
+ return newLastModified;
+ }
+
+ private long touchFile(final CompileMojo compileMojo, String file2Touch)
+ throws InterruptedException {
+ File lessFile = new File(compileMojo.sourceDirectory, file2Touch);
+
+ /*
+ * The last modified value might be rounded down on a second by the
+ * platform. This way the last compilation and the touch may end up with
+ * the same millis avoiding the compilation. To prevent that the thread
+ * has to sleep a second.
+ */
+ Thread.sleep(1000);
+ long currentMillies = System.currentTimeMillis();
+ // touch the less file
+ lessFile.setLastModified(currentMillies);
+ return currentMillies;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/lesscss/mojo/ConcatenateTest.java b/src/test/java/org/lesscss/mojo/ConcatenateTest.java
new file mode 100644
index 0000000..8620250
--- /dev/null
+++ b/src/test/java/org/lesscss/mojo/ConcatenateTest.java
@@ -0,0 +1,38 @@
+package org.lesscss.mojo;
+
+import java.io.File;
+
+import org.apache.maven.plugin.testing.AbstractMojoTestCase;
+import org.junit.Test;
+
+public class ConcatenateTest extends AbstractMojoTestCase {
+
+ @Test
+ public void testDontUpdateIfNotChanged() throws Exception {
+ File pom = getTestFile("src/test/resources/concatenate-pom.xml");
+ final CompileMojo compileMojo = (CompileMojo) lookupMojo("compile", pom);
+
+ File expected = new File(compileMojo.sourceDirectory, "concatenated.less");
+ if(expected.exists()){
+ expected.delete();
+ }
+
+ compileMojo.execute();
+ expected = new File(compileMojo.sourceDirectory, "concatenated.less");
+
+ compileMojo.execute();
+ File actual = new File(compileMojo.sourceDirectory, "concatenated.less");
+
+ assertEquals(expected.lastModified(), actual.lastModified());
+ }
+
+ @Test
+ public void testProducesCssFile() throws Exception{
+ File pom = getTestFile("src/test/resources/concatenate-pom.xml");
+ final CompileMojo compileMojo = (CompileMojo) lookupMojo("compile", pom);
+ compileMojo.execute();
+
+ File expected = new File(compileMojo.outputDirectory, "concatenated.css");
+ assertTrue(expected.exists());
+ }
+}
diff --git a/src/test/java/org/lesscss/mojo/MultipleConfigurationTest.java b/src/test/java/org/lesscss/mojo/MultipleConfigurationTest.java
new file mode 100644
index 0000000..b95480d
--- /dev/null
+++ b/src/test/java/org/lesscss/mojo/MultipleConfigurationTest.java
@@ -0,0 +1,124 @@
+package org.lesscss.mojo;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.maven.plugin.testing.AbstractMojoTestCase;
+import org.junit.Test;
+
+public class MultipleConfigurationTest extends AbstractMojoTestCase {
+
+ private CompileMojo mojo;
+
+ private File sourceDirectory = new File("./source");
+ private File outputDirectory = new File("./output");
+ private String[] includes = new String[] { "include" };
+ private String[] excludes = new String[] { "exclude" };
+ private boolean compress = true;
+ private boolean watch = true;
+ private int watchInterval = 3600;
+ private String encoding = "UTF-8";
+ private boolean force = true;
+ private File lessJs = new File("./lessJs");
+ private String concatenateTo = "test.css";
+
+ private String[] files = new String[] { "file" };
+
+ @Test
+ public void testNeverReturnNullConfigurations() {
+ mojo = new CompileMojo();
+ List configurationItems = mojo.getConfiguration();
+ assertNotNull(configurationItems);
+ assertEquals(0, configurationItems.size());
+ }
+
+ @Test
+ public void testGetSimpleSingleConfiguration() throws IllegalAccessException {
+ mojo = new CompileMojo();
+
+ setVariableValueToObject(mojo, "sourceDirectory", sourceDirectory);
+ setVariableValueToObject(mojo, "outputDirectory", outputDirectory);
+ setVariableValueToObject(mojo, "includes", includes);
+ setVariableValueToObject(mojo, "excludes", excludes);
+ setVariableValueToObject(mojo, "compress", compress);
+ setVariableValueToObject(mojo, "watch", watch);
+ setVariableValueToObject(mojo, "watchInterval", watchInterval);
+ setVariableValueToObject(mojo, "force", force);
+ setVariableValueToObject(mojo, "lessJs", lessJs);
+ setVariableValueToObject(mojo, "concatenateTo", concatenateTo);
+
+ List configurationItems = mojo.getConfiguration();
+ assertEquals(1, configurationItems.size());
+
+ ConfigurationItem item = configurationItems.get(0);
+ assertEquals(sourceDirectory.getAbsolutePath(), item.getSourceDirectory()
+ .getAbsolutePath());
+ assertEquals(outputDirectory.getAbsolutePath(), item.getOutputDirectory()
+ .getAbsolutePath());
+ assertEquals(includes, item.getIncludes());
+ assertEquals(excludes, item.getExcludes());
+ assertEquals(compress, item.isCompress());
+ assertEquals(watch, item.isWatch());
+ assertEquals(watchInterval, item.getWatchInterval());
+ assertEquals(force, item.isForce());
+ assertEquals(lessJs, item.getLessJs());
+ assertEquals(concatenateTo, item.getConcateanteTo());
+ }
+
+ @Test
+ public void testSingleConfigurationItem() throws IllegalAccessException {
+ ConfigurationItem configurationItem = new ConfigurationItem();
+ configurationItem.setExcludes(excludes);
+ configurationItem.setIncludes(includes);
+ configurationItem.setOutputDirectory(outputDirectory);
+ configurationItem.setSourceDirectory(sourceDirectory);
+ List original = new ArrayList();
+ original.add(configurationItem);
+
+ mojo = new CompileMojo();
+ setVariableValueToObject(mojo, "configurationItems", original);
+
+ List actual = mojo.getConfiguration();
+ assertEquals(1, actual.size());
+
+ ConfigurationItem item = actual.get(0);
+ assertEquals(sourceDirectory.getAbsolutePath(), item.getSourceDirectory()
+ .getAbsolutePath());
+ assertEquals(outputDirectory.getAbsolutePath(), item.getOutputDirectory()
+ .getAbsolutePath());
+ assertEquals(includes, item.getIncludes());
+ assertEquals(excludes, item.getExcludes());
+ }
+
+ @Test
+ public void testTwoConfigurationItems() throws IllegalAccessException {
+ ConfigurationItem configurationItem = new ConfigurationItem();
+ configurationItem.setExcludes(excludes);
+ configurationItem.setIncludes(includes);
+ configurationItem.setOutputDirectory(outputDirectory);
+ configurationItem.setSourceDirectory(sourceDirectory);
+ List original = new ArrayList();
+ original.add(configurationItem);
+ original.add(configurationItem); // add twice
+
+ mojo = new CompileMojo();
+ setVariableValueToObject(mojo, "configurationItems", original);
+
+ List actual = mojo.getConfiguration();
+ assertEquals(2, actual.size());
+ }
+
+ @Test
+ public void testReadConfigurationFromPom() throws Exception {
+ File pom = getTestFile("src/test/resources/multiple-pom.xml");
+ assertNotNull(pom);
+ assertTrue(pom.exists());
+ final CompileMojo compileMojo = (CompileMojo) lookupMojo("compile", pom);
+ assertNotNull(compileMojo);
+ List config = compileMojo.getConfiguration();
+ assertEquals(2, config.size());
+ assertTrue(config.get(0).getOutputDirectory().getAbsolutePath().endsWith("css1"));
+ assertTrue(config.get(1).getOutputDirectory().getAbsolutePath().endsWith("css2"));
+ }
+}
diff --git a/src/test/resources/change-pom.xml b/src/test/resources/change-pom.xml
new file mode 100644
index 0000000..abc103f
--- /dev/null
+++ b/src/test/resources/change-pom.xml
@@ -0,0 +1,32 @@
+
+
+ 4.0.0
+ org.lesscss.it
+ includes
+ testing
+
+
+
+ org.lesscss
+ lesscss-maven-plugin
+ @project.version@
+
+ src/test/resources/less
+ true
+
+ bootstrap.less
+
+
+
+
+
+ compile
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/concatenate-pom.xml b/src/test/resources/concatenate-pom.xml
new file mode 100644
index 0000000..f4b97f1
--- /dev/null
+++ b/src/test/resources/concatenate-pom.xml
@@ -0,0 +1,37 @@
+
+
+ 4.0.0
+ org.lesscss.it
+ basic
+ testing
+
+
+
+
+ org.lesscss
+ lesscss-maven-plugin
+ @project.version@
+
+ concatenated.less
+ src/test/resources/less
+ ${project.build.directory}/css1
+
+ **/*.less
+
+
+ less.less
+
+
+
+
+
+ compile
+
+
+
+
+
+
+
+
diff --git a/src/test/resources/less/1/reset.less b/src/test/resources/less/1/reset.less
new file mode 100644
index 0000000..3dd9031
--- /dev/null
+++ b/src/test/resources/less/1/reset.less
@@ -0,0 +1,134 @@
+//
+// Modals
+// Adapted from http://github.com/necolas/normalize.css
+// --------------------------------------------------
+
+
+// Display in IE6-9 and FF3
+// -------------------------
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+nav,
+section {
+ display: block;
+}
+
+// Display block in IE6-9 and FF3
+// -------------------------
+
+audio,
+canvas,
+video {
+ display: inline-block;
+ *display: inline;
+ *zoom: 1;
+}
+
+// Prevents modern browsers from displaying 'audio' without controls
+// -------------------------
+
+audio:not([controls]) {
+ display: none;
+}
+
+// Base settings
+// -------------------------
+
+html {
+ font-size: 100%;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+}
+
+// Hover & Active
+a:hover,
+a:active {
+ outline: 0;
+}
+
+// Prevents sub and sup affecting line-height in all browsers
+// -------------------------
+
+sub,
+sup {
+ position: relative;
+ font-size: 75%;
+ line-height: 0;
+ vertical-align: baseline;
+}
+sup {
+ top: -0.5em;
+}
+sub {
+ bottom: -0.25em;
+}
+
+// Img border in a's and image quality
+// -------------------------
+
+img {
+ /* Responsive images (ensure images don't scale beyond their parents) */
+ max-width: 100%; /* Part 1: Set a maxium relative to the parent */
+ width: auto\9; /* IE7-8 need help adjusting responsive images */
+ height: auto; /* Part 2: Scale the height according to the width, otherwise you get stretching */
+
+ vertical-align: middle;
+ border: 0;
+ -ms-interpolation-mode: bicubic;
+}
+
+// Prevent max-width from affecting Google Maps
+#map_canvas img {
+ max-width: none;
+}
+
+// Forms
+// -------------------------
+
+// Font size in all browsers, margin changes, misc consistency
+button,
+input,
+select,
+textarea {
+ margin: 0;
+ font-size: 100%;
+ vertical-align: middle;
+}
+button,
+input {
+ *overflow: visible; // Inner spacing ie IE6/7
+ line-height: normal; // FF3/4 have !important on line-height in UA stylesheet
+}
+button::-moz-focus-inner,
+input::-moz-focus-inner { // Inner padding and border oddities in FF3/4
+ padding: 0;
+ border: 0;
+}
+button,
+input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+ cursor: pointer; // Cursors on all buttons applied consistently
+ -webkit-appearance: button; // Style clickable inputs in iOS
+}
+input[type="search"] { // Appearance in Safari/Chrome
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ -webkit-appearance: textfield;
+}
+input[type="search"]::-webkit-search-decoration,
+input[type="search"]::-webkit-search-cancel-button {
+ -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5
+}
+textarea {
+ overflow: auto; // Remove vertical scrollbar in IE6-9
+ vertical-align: top; // Readability and alignment cross-browser
+}
\ No newline at end of file
diff --git a/src/test/resources/less/2/21/variables.less b/src/test/resources/less/2/21/variables.less
new file mode 100644
index 0000000..cd37886
--- /dev/null
+++ b/src/test/resources/less/2/21/variables.less
@@ -0,0 +1,282 @@
+// Variables
+// --------------------------------------------------
+
+
+// Global values
+// --------------------------------------------------
+
+
+// Grays
+// -------------------------
+@black: #000;
+@grayDarker: #222;
+@grayDark: #333;
+@gray: #555;
+@grayLight: #999;
+@grayLighter: #eee;
+@white: #fff;
+
+
+// Accent colors
+// -------------------------
+@blue: #049cdb;
+@blueDark: #0064cd;
+@green: #46a546;
+@red: #9d261d;
+@yellow: #ffc40d;
+@orange: #f89406;
+@pink: #c3325f;
+@purple: #7a43b6;
+
+
+// Scaffolding
+// -------------------------
+@bodyBackground: yellow;
+@textColor: yellow;
+//#c0c0c0;
+
+
+// Links
+// -------------------------
+@linkColor: #c0c0c0;
+@linkColorHover: darken(@linkColor, 15%);
+
+
+// Typography
+// -------------------------
+@sansFontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif;
+@serifFontFamily: Georgia, "Times New Roman", Times, serif;
+@monoFontFamily: Monaco, Menlo, Consolas, "Courier New", monospace;
+
+@baseFontSize: 14px;
+@baseFontFamily: @sansFontFamily;
+@baseLineHeight: 20px;
+@altFontFamily: @serifFontFamily;
+
+@headingsFontFamily: inherit; // empty to use BS default, @baseFontFamily
+@headingsFontWeight: bold; // instead of browser default, bold
+@headingsColor: inherit; // empty to use BS default, @textColor
+
+
+// Tables
+// -------------------------
+@tableBackground: transparent; // overall background-color
+@tableBackgroundAccent: #f9f9f9; // for striping
+@tableBackgroundHover: #f5f5f5; // for hover
+@tableBorder: #ddd; // table and cell border
+
+
+// Buttons
+// -------------------------
+@btnBackground: @white;
+@btnBackgroundHighlight: darken(@white, 10%);
+@btnBorder: #bbb;
+
+@btnPrimaryBackground: @linkColor;
+@btnPrimaryBackgroundHighlight: spin(@btnPrimaryBackground, 20%);
+
+@btnInfoBackground: #5bc0de;
+@btnInfoBackgroundHighlight: #2f96b4;
+
+@btnSuccessBackground: #62c462;
+@btnSuccessBackgroundHighlight: #51a351;
+
+@btnWarningBackground: lighten(@orange, 15%);
+@btnWarningBackgroundHighlight: @orange;
+
+@btnDangerBackground: #ee5f5b;
+@btnDangerBackgroundHighlight: #bd362f;
+
+@btnInverseBackground: #444;
+@btnInverseBackgroundHighlight: @grayDarker;
+
+
+// Forms
+// -------------------------
+@inputBackground: @white;
+@inputBorder: #ccc;
+@inputBorderRadius: 3px;
+@inputDisabledBackground: @grayLighter;
+@formActionsBackground: #f5f5f5;
+
+// Dropdowns
+// -------------------------
+@dropdownBackground: @white;
+@dropdownBorder: rgba(0,0,0,.2);
+@dropdownDividerTop: #e5e5e5;
+@dropdownDividerBottom: @white;
+
+@dropdownLinkColor: @grayDark;
+
+@dropdownLinkColorHover: @white;
+@dropdownLinkBackgroundHover: @dropdownLinkBackgroundActive;
+
+@dropdownLinkColorActive: @dropdownLinkColor;
+@dropdownLinkBackgroundActive: @linkColor;
+
+
+
+// COMPONENT VARIABLES
+// --------------------------------------------------
+
+// Z-index master list
+// -------------------------
+// Used for a bird's eye view of components dependent on the z-axis
+// Try to avoid customizing these :)
+@zindexDropdown: 1000;
+@zindexPopover: 1010;
+@zindexTooltip: 1030;
+@zindexFixedNavbar: 1030;
+@zindexModalBackdrop: 1040;
+@zindexModal: 1050;
+
+
+// Sprite icons path
+// -------------------------
+@iconSpritePath: "../img/glyphicons-halflings.png";
+@iconWhiteSpritePath: "../img/glyphicons-halflings-white.png";
+
+
+// Input placeholder text color
+// -------------------------
+@placeholderText: @grayLight;
+
+
+// Hr border color
+// -------------------------
+@hrBorder: @grayLighter;
+
+
+// Horizontal forms & lists
+// -------------------------
+@horizontalComponentOffset: 180px;
+
+
+// Wells
+// -------------------------
+@wellBackground: #f5f5f5;
+
+
+// Navbar
+// -------------------------
+@navbarCollapseWidth: 979px;
+
+@navbarHeight: 40px;
+@navbarBackground: darken(@navbarBackgroundHighlight, 5%);
+@navbarBackgroundHighlight: #ffffff;
+@navbarBorder: darken(@navbarBackground, 12%);
+
+@navbarText: @gray;
+@navbarLinkColor: @gray;
+@navbarLinkColorHover: @grayDark;
+@navbarLinkColorActive: @gray;
+@navbarLinkBackgroundHover: transparent;
+@navbarLinkBackgroundActive: darken(@navbarBackground, 5%);
+
+@navbarBrandColor: @navbarLinkColor;
+
+// Inverted navbar
+@navbarInverseBackground: #111111;
+@navbarInverseBackgroundHighlight: #222222;
+@navbarInverseBorder: #252525;
+
+@navbarInverseText: @grayLight;
+@navbarInverseLinkColor: @grayLight;
+@navbarInverseLinkColorHover: @white;
+@navbarInverseLinkColorActive: @navbarInverseLinkColorHover;
+@navbarInverseLinkBackgroundHover: transparent;
+@navbarInverseLinkBackgroundActive: @navbarInverseBackground;
+
+@navbarInverseSearchBackground: lighten(@navbarInverseBackground, 25%);
+@navbarInverseSearchBackgroundFocus: @white;
+@navbarInverseSearchBorder: @navbarInverseBackground;
+@navbarInverseSearchPlaceholderColor: #ccc;
+
+@navbarInverseBrandColor: @navbarInverseLinkColor;
+
+
+// Pagination
+// -------------------------
+@paginationBackground: #fff;
+@paginationBorder: #ddd;
+@paginationActiveBackground: #f5f5f5;
+
+
+// Hero unit
+// -------------------------
+@heroUnitBackground: @grayLighter;
+@heroUnitHeadingColor: inherit;
+@heroUnitLeadColor: inherit;
+
+
+// Form states and alerts
+// -------------------------
+@warningText: #c09853;
+@warningBackground: #fcf8e3;
+@warningBorder: darken(spin(@warningBackground, -10), 3%);
+
+@errorText: #b94a48;
+@errorBackground: #f2dede;
+@errorBorder: darken(spin(@errorBackground, -10), 3%);
+
+@successText: #468847;
+@successBackground: #dff0d8;
+@successBorder: darken(spin(@successBackground, -10), 5%);
+
+@infoText: #3a87ad;
+@infoBackground: #d9edf7;
+@infoBorder: darken(spin(@infoBackground, -10), 7%);
+
+
+// Tooltips and popovers
+// -------------------------
+@tooltipColor: #fff;
+@tooltipBackground: #000;
+@tooltipArrowWidth: 5px;
+@tooltipArrowColor: @tooltipBackground;
+
+@popoverBackground: #fff;
+@popoverArrowWidth: 10px;
+@popoverArrowColor: #fff;
+@popoverTitleBackground: darken(@popoverBackground, 3%);
+
+// Special enhancement for popovers
+@popoverArrowOuterWidth: @popoverArrowWidth + 1;
+@popoverArrowOuterColor: rgba(0,0,0,.25);
+
+
+
+// GRID
+// --------------------------------------------------
+
+
+// Default 940px grid
+// -------------------------
+@gridColumns: 12;
+@gridColumnWidth: 60px;
+@gridGutterWidth: 20px;
+@gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1));
+
+// 1200px min
+@gridColumnWidth1200: 70px;
+@gridGutterWidth1200: 30px;
+@gridRowWidth1200: (@gridColumns * @gridColumnWidth1200) + (@gridGutterWidth1200 * (@gridColumns - 1));
+
+// 768px-979px
+@gridColumnWidth768: 42px;
+@gridGutterWidth768: 20px;
+@gridRowWidth768: (@gridColumns * @gridColumnWidth768) + (@gridGutterWidth768 * (@gridColumns - 1));
+
+
+// Fluid grid
+// -------------------------
+@fluidGridColumnWidth: percentage(@gridColumnWidth/@gridRowWidth);
+@fluidGridGutterWidth: percentage(@gridGutterWidth/@gridRowWidth);
+
+// 1200px min
+@fluidGridColumnWidth1200: percentage(@gridColumnWidth1200/@gridRowWidth1200);
+@fluidGridGutterWidth1200: percentage(@gridGutterWidth1200/@gridRowWidth1200);
+
+// 768px-979px
+@fluidGridColumnWidth768: percentage(@gridColumnWidth768/@gridRowWidth768);
+@fluidGridGutterWidth768: percentage(@gridGutterWidth768/@gridRowWidth768);
\ No newline at end of file
diff --git a/src/test/resources/less/bootstrap.less b/src/test/resources/less/bootstrap.less
new file mode 100644
index 0000000..c11f3d1
--- /dev/null
+++ b/src/test/resources/less/bootstrap.less
@@ -0,0 +1,20 @@
+/*!
+ * Bootstrap v2.1.1
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+/*
+Attention:
+Any changes of this file will be lost on the next compile run of the .less files!
+ */
+
+// CSS Reset
+@import "1/reset.less";
+
+// Core variables and mixins
+@import "2/21/variables.less"; // Modify this for custom colors, font-sizes, etc
\ No newline at end of file
diff --git a/src/test/resources/multiple-pom.xml b/src/test/resources/multiple-pom.xml
new file mode 100644
index 0000000..b275304
--- /dev/null
+++ b/src/test/resources/multiple-pom.xml
@@ -0,0 +1,38 @@
+
+
+ 4.0.0
+ org.lesscss.it
+ basic
+ testing
+
+
+
+
+ org.lesscss
+ lesscss-maven-plugin
+ @project.version@
+
+
+
+ ${project.basedir}/src/main/less1
+ ${project.build.directory}/css1
+
+
+ ${project.basedir}src/main/less2
+ ${project.build.directory}/css2
+
+
+
+
+
+
+ compile
+
+
+
+
+
+
+
+