Compiling Clojurescript

November 29, 2017

I am making little web games in Clojurescript and I want to be able to put them up on github so my friends can play them. So far I have been working in a live-reloading-dev environment so I haven't given this problem any thought.

After a bit of fuss, trying to read lots of tutorials and looking at code my partner wrote for one of our projects, I figured it out. I'm writing it up here for two reasons.

  1. So I can retrace my steps next time I need to set up compilation code in a Clojurescript project (it can vary each time!).

  2. For anyone else who is looking for help.

I looked at these tutorials but neither gave me a working answer out-of-the-box.

Don't get me wrong - the guys who wrote these have created tons of awesome resources that are very accessible!

Here's what worked for me!

Make sure I have the following:

  • source files (this is usually a directory and can contain multiple files that you want to be compiled)
  • cljs.jar
  • build.clj
  • start.sh

The cljs.jar is a big library. I'm not 100% sure it's a library because I just needed to make sure it was in the right place after I downloaded it. I didn't need to edit it or open it.

The build.clj file is the clojure code that specifies what you want compiled. Mine contained:

1(require 'cljs.build.api)
2(cljs.build.api/build "src" {:output-to "resources/public/js/outs/main.js"})

src is the directory path that contains everything I want compiled.

The string after :output-to is the directory path and file name that I want created. (It needs to be a javascript file because that's what we're outputting! But I haven't tried anything else so I'm not sure what mischief might occur.)

The directory paths are relative to the location of the build.clj file. (BTW, you can name the build file other things, but "build" or "builds" seems common.)

The start.sh file makes using the terminal easier. It contains:

1#!/bin/bash
2JVM_OPTIONS="
3  -cp `lein classpath`
4  -Djava.awt.headless=true
5  -client
6  -XX:+UseConcMarkSweepGC
7  -XX:+CMSClassUnloadingEnabled"
8rlwrap java $JVM_OPTIONS clojure.main build.clj development

I think the first line says, "hey, we're talking in bash" so the terminal or shell (what's the difference?) knows which language we're in. I owe Kevin Lynagh for writing this file and most of this app set up.

I forget what rlwrap does. I use it for starting figwheel, too.

  • java is the command or program we're invoking.

  • The $JVM_OPTIONS looks like a list of options for invoking java.

  • clojure.main is not something I understand.

  • build.clj is the name of the build file you want to run.

  • development is…?

Now that I have these files in place, I navigate to my project directory in the Terminal and run ./start.sh

This gets everything going!

You should see a folder labeled out in your current directory. This contains all the code for the dependencies not in your source. You should also be able to find the .js file you defined as the output of your build.clj file.

You'll need an index.html file that contains:

 1  <html>
 2    <head>
 3      <meta charset="utf-8">
 4      <link href="css/style.css" rel="stylesheet" type="text/css">
 5    </head>
 6    <body>
 7      <div id="container"></div>
 8      <script type="text/javascript" src="js/out/goog/base.js"></script>
 9      <script src="js/outs/main.js"></script>
10      <script type="text/javascript">
11          goog.require("com.nicki.flipside.main");
12          // Note the underscore "_"!
13      </script>
14    </body>
15  </html>

There are three scripts in here. One is for the .js that you just output. Its source path is relative to the index.html file Another script is for directing the javascript file to the files in needs in goog. This is some really basic dependency stuff. The last script connects the other two scripts. You need all these things for your app to run.

I did a little fiddling around later to reorganize the output. I want to be able to easily copy a directory that contains the complete build. In the future, I'd like to be able to present a series of versions on the same website so I could add each of these as iFrames or combine their index.html files somehow.

I was also getting an "out" folder on my main directory and I'd rather see it in the directory that contains the build because the build needs these files. You can specify this with another compile option in your build.clj file:

1  :output-dir "resources/public/js/out"

Here's the updated build.clj file:

1(require 'cljs.build.api)
2
3(cljs.build.api/build "src" {:output-to "resources/public/js/flipside.js"
4                             :output-dir "resources/public/js/out"})

Thanks for reading! Let me know if this is helpful or if you have any questions.