Skip to content

Instantly share code, notes, and snippets.

@ankon
Last active November 12, 2019 10:35
Show Gist options
  • Save ankon/402d13252b3a103fe21c4612827c8547 to your computer and use it in GitHub Desktop.
Save ankon/402d13252b3a103fe21c4612827c8547 to your computer and use it in GitHub Desktop.
VSCode: Incremental build with maven reactors
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "0.1.0",
"command": "gulp",
"isShellCommand": true,
"suppressTaskName": true,
"tasks": [
{
"taskName": "compile-server",
"args": [ "java:compile-server" ],
"showOutput": "silent",
"isBuildCommand": true,
"isWatching": true,
"problemMatcher": {
"owner": "java",
"fileLocation": "absolute",
"pattern": [
{
"regexp": "^\\[(ERROR|WARNING|INFO)\\]\\s(.+\\.java):\\[(\\d+,\\d+)\\]\\s(.+)$",
"file": 2,
"location": 3,
"severity": 1,
"message": 4
}
]
}
}
]
}
const gulp = require('gulp');
const watch = require('gulp-watch');
const path = require('path');
const through2 = require('through2');
const spawn = require('child_process').spawn;
const File = require('vinyl');
// Map of project name to object:
// .last: last time it was compiled, to debounce the 'compile-server' logic
// .status: last know result (error/success)
// .running: if true, then a compile is running for this project.
const projects = {};
function getProjectName(file) {
const segments = file.relative.split(path.sep);
if (segments.length > 1) {
return segments[0];
} else {
return null;
}
}
function createProject(name) {
return { last: 0, running: false, status: 'unknown' };
}
/**
* Run a maven build for each input `pom.xml`
*
* If the `pom.xml` is a module maven will be executed with `-pl <module>`, otherwise
* it is assumed that this is the root project.
*
* @param {boolean} [debounce] if `true` then skip silently if this has been requested recently already (ignored if this is a not a project)
*/
function runMaven(debounce = false) {
return through2.obj(function(file, enc, cb) {
if (path.basename(file.path) !== 'pom.xml') {
// Ignore non-pom.xmls
return cb();
}
const args = ['-q', '-B', 'compiler:compile', 'compiler:testCompile' ];
let project;
const projectName = getProjectName(file);
if (projectName) {
project = projects[projectName];
if (project.running) {
return cb();
}
const now = Date.now();
if (!!debounce && now - project.last < 5000) {
return cb();
}
project.running = true;
project.last = now;
args.push('-pl', projectName);
} else {
args.push('--fail-at-end');
}
const mvn = spawn('mvn', args, { shell: true });
// FIXME: Maven prints the same error multiple times for reactor projects -- once in the project, and then in the summary.
// We really only need the error once, until VSC dedups things on its own (problemCollectors.ts)
mvn.stderr.pipe(process.stderr);
mvn.stdout.pipe(process.stdout);
mvn.on('close', (code) => {
if (project) {
project.running = false;
project.status = code === 0 ? 'success' : 'failure';
}
return cb();
});
});
}
// Compile all files once
gulp.task('java:compile-initial', function() {
function initializeProjects() {
return through2.obj(function(file, enc, cb) {
const projectName = getProjectName(file);
projects[projectName] = createProject();
return cb(null);
}, function(cb) {
this.push(new File({
cwd: process.cwd(),
base: process.cwd(),
path: "pom.xml",
}));
return cb(null);
});
}
return gulp.src('*/pom.xml')
.pipe(initializeProjects())
.pipe(runMaven());
});
// Task that runs continously, and feeds changed files into the 'java:compile-project' task
gulp.task('java:compile-server', [ 'java:compile-initial' ], function() {
return watch('*/src/**/*.java', { read: false }, function(f) {
// Determine the project
const projectName = getProjectName(f);
return gulp.src(path.join(projectName, 'pom.xml'), { read: false })
.pipe(runMaven(true));
});
});
{
"name": "...",
"version": "...",
"devDependencies": {
"gulp": "^3.9.1",
"gulp-watch": "^4.3.8",
"through2": "^2.0.1"
}
}
@ankon
Copy link
Author

ankon commented Jul 12, 2016

Issues:

  • severity markers seem to be matched case-sensitive, so only by luck do we end up with Severity.Error for 'ERROR'
  • markers are duplicated, because maven produces the errors twice (once additionally at the end of the reactor), and VSCode doesn't deduplicate them
  • markers are never cleared (unknown how to actually do that, likely also a bug-let in VSCode)
  • somehow the 'column' part of the location doesn't work properly, might be that VSCode and/or java treat tabs as a fixed amount of spaces, and column is not a character index.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment