On 17/05/2012, at 5:48 AM, Luke Daley wrote: > I've been surveying the Javascript tooling landscape recently. > > One thing that's very common is to have daemon processes that watch files and > perform some action on change. Consider the case where you want to write > CoffeeScript, but compile it down to JavaScript and deploy that. During the > development cycle, you want something to do this compilation as soon as you > save a change so that when you refresh the browser you see the change > (because the javascript has been updated). > > With javascript, there can be a few different processing phases. e.g. > CoffeeScript → JavaScript → compressed JavaScript → bundled JavaScript > (combined into one file). You way want to use this final output during the > development process. > > Another case for this is continuous testing. A lot of the javascript testing > tools require a html bootstrapping page that does something like set the test > classpath and kick off the tests. You really want this to be generated, > managing the “classpath” dynamically. > > All of the maven javascript tooling plugins that do this hand roll their own > daemon process management, which is usually just starting the process. > > There are non javascript cases for this too. > > For war development, it would be nice to have something like > “processResources” run whenever an input file changes (as they may be > filtered). For our html slides, I'd like them to be compiled to html whenever > I changed the input markdown. > > It strikes me that we may be able to come up with a solution that could turn > almost any task into what I'm calling a “live task”. Since we know what the > inputs are we could monitor them and trigger the task when they change, no > matter what the task is doing. I suspect that would be the easy part though. > Managing and providing control over these daemon processes would likely be > the most costly to implement, as well as providing insight into what they are > doing. > > Rather exciting possibility though. If I'm working in a java module with unit > tests, I could just “liven” the test task for that module and achieve > continuous testing (granted, you'd want to be plugged into a power source if > you did this). I'm not aware of another tool that provides this feature > generically, for any kind of task.
This is an interesting idea. There are heaps of use cases for this, or variations of this. First there's all the above 'I'm working on blah, and I want blah to always be usable' cases: keeping a web app (javascript based or otherwise), or documentation, or resources for tests, or whatever, up-to-date and usable. Then there are the feedback use cases: 'let me know when I've broken a unit test', or 'let me know when I've forgotten to add a license header'. There are also use cases in the IDE: 'let me know when a new version of a dependency is available', and pushing model changes into the IDE in general. Another interesting use case is in deployment, where you want to say: 'here is the deployment model for this application, do what you need to do to keep the application up-to-date wrt the model'. Implementation-wise, there are a very diverse set of inputs that are considered when you run 'gradle someTask', that we'd have to ultimately consider in a long running process: * Changes in the build environment declared in places like ~/.gradle/gradle.properties or $rootDir/gradle/wrapper/gradle-wrapper.properties. This includes changes to which JVM to use, which version of Gradle to use. * Changes in the model logic, in various places such as init scripts, settings.gradle, build.gradle, buildSrc, and the various script class paths. * Changes in the model inputs, such as gradle.properties, or things like user credentials stored in a key store. * Changes in transitive inputs of the tasks, from various locations, such as source files, modules in repositories, and so on. * Changes in the inputs of the tasks themselves. I would not think of this as 'live tasks'. Instead, I would model this as keeping some output up-to-date, and let Gradle decide which tasks are appropriate. For example, rather than say 'please rerun the jettyRun task each time one of its inputs change', you'd say 'please keep my web app up-to-date'. First, Gradle would run 'startJetty' and 'jettyDeploy' (assuming we've refactored the jetty plugin to be a little more usable). Then, it would watch for changes to the web application. On a change to the source of the web app, Gradle would run 'jettyUndeploy', 'jettyDeploy' (plus dependencies, of course). On a change to deployment configuration, Gradle would run 'jettyStop', 'jettyStart', 'jettyDeploy'. In other words, the outputs are the most important thing in the Gradle world, not the tasks. We can do far more interesting things if we model things in terms of outputs (i.e. usable artifacts), rather than tasks. As far as the UI goes, I wouldn't bother with a real daemon to start with. We could instead have something simple like a --watch-for-changes (bad name, I know) command-line option, which would mean that Gradle builds the things mentioned on the command-line, then continues to run in the foreground, watching for changes and doing the appropriate work as things happen. It could still use a Gradle daemon to actually do the work, so that we can also make the 'watch for changes and do something useful on change' capability available to the IDE via the tooling API. There is a lot of overlap here with stuff we want to do for other reasons: * We need some kind of monitoring capability for the tooling API, to notify the IDE about changes to the model. This would need to be baked into the daemon in some form. * We need to track inputs to build logic, so that we can skip configuring parts of the model that haven't changed, for various performance reasons: to make configuration faster for large builds, to make IDE import faster, and to make command-line tab completion a reality. * We need to model the things that the build produces as first class citizens, for a bunch of reasons: better c++ support, better API/implementation and compile/runtime classpath separation, skip configuration for outputs that haven't changed, parallel execution, build promotion and release management. So, I'd be tempted to wait until we have some of this stuff in place before tackling the problem of keeping things continuously up-to-date. -- Adam Murdoch Gradle Co-founder http://www.gradle.org VP of Engineering, Gradleware Inc. - Gradle Training, Support, Consulting http://www.gradleware.com
