dirtyvagabond edited this page Oct 27, 2010 · 11 revisions

Q: Why don’t you just use Lein or Maven like everyone else?
A: The projects we work on are fairly complex. We couldn’t do what we needed easily with Lein (believe me, we spent a lot of time trying), and we weren’t willing to write and maintain tons of XML either. Also, we got sick of waiting for the JVM to startup.

Q: There was an error in some of my code and cake won’t respond now. What should I do?
A: The persistent JVM may get into a bad state if there are errors in your project code or in dependencies. In this case you can manually shutdown cake. In this order, try:

  • cake stop
  • cake kill
  • cake kill -9

Q: There are a bunch of processes when I run cake ps. Where did they come from?
A: Cake starts two JVMs for each project that you run cake inside. Also, if you run cake from somewhere that isn’t inside a project dir, it will start a JVM in your home directory.

Q: How does cake determine whether you are in a project directory?
A: Cake recursively searches backwards up your current path looking for any of the following files: project.clj, build.clj, or .cake/

Q: Why does cake start two JVMs per project?
A: So that Cake’s dependencies don’t interfere with your project’s dependencies, cake starts one JVM for itself and one JVM for you project (colloquially referred to as the bake JVM). If there is no project.clj file in your project, cake only starts one JVM.

Q: I’ve got some code that runs in another thread (i.e. (future (println "foo"))). Why can’t I see the thread’s stderr or stdout in the repl?
A: It isn’t possible for threads to print in the repl because there could be multiple repls running and there is no way to tell which threads belong to which repl. However, all non-interactive threads print to .cake/project.log. You can tail this file directly, or you can tail it when you start cake with cake start -l or cake restart -l.

Q: Why do cake commands sometimes take a long time to start?
A: Cake commands usually have no startup time, because cake connects to a persistent JVM. Cake tries to keep this JVM running as long as possible, even reloading .clj files that change. However, when .jar and .class files in your classpath change, Cake restarts the JVM so the classloader can load the new files. This is infrequent, but when it happens, cake commands have the usual JVM startup costs.

Q: Why does Cake use Ruby?
A: Because we needed to write the client script is some widely available scripting language, and bash got way too hairy way too fast. Besides, Ruby is arguably more platform-independent than bash. Also, we only use Ruby libraries that are included in the core distribution so Cake can work as many places as possible.

Q: Can I use JRuby?
A: We use fork and exec to start the persistent JVMs. JRuby doesn’t support fork by default, but you could probably get it working if you wanted to. We haven’t done it though, because JRuby still has to start the JVM every time you invoke cake, which is too slow for us.

Q: Why don’t you use function calls to specify task dependencies, like Lein?
A: We think a dependency-based model is easier to use and more powerful. This model has worked very well for make, rake, ant, and many other build tools. There are also a few technical reasons why dependencies are better:

  1. Specifying dependencies makes tasks much easier to parallelize.
  2. There is no way for users to add dependencies to an existing task if they are just function calls within the task body.

For example, if I have a project that needs to compile protocol buffers before running the compile task, making the compile task dependent on the proto task is an easy way to do this. If all I can do is add actions to be performed after existing task actions, there is no clean way to add this dependency. I could find another task that compiles calls and add it there, or create pre- and post- hook tasks for every task, but that’s overly complicated. There’s a good section in Martin Fowler’s article about rake that explains our philosophy.

Q: Why do you use sets to specify dependencies?
A: Using sets makes it clear that you shouldn’t rely on the order of the dependencies. In the following example, data-load could happen before compile. This leaves open the future possibility of optimizing performance by having multiple threads execute dependencies in parallel.

(deftask test #{compile data-load}
  "Run tests"

Q: What if I want my dependencies to be executed in a specific order?
A: You should specify the dependencies explicitly. So in the above example, if data-load depends on compile, then add the dependency.

(deftask data-load #{compile})