Compiling Unity projects from within Emacs

I’m often asked how I get Emacs to compile up Unity projects*, and I thought it would be worthwhile writing a proper post on what I’ve done to make this a relatively painless process.

Once you’ve followed this post you should get syntax highlighting, in-Emacs compiles and on-the-fly syntax checking, as well as autocompletion and refactoring. I should say that this *doesn’t* stop Unity re-compiling your files when you switch back to it – I’ve not made any attempt to unify them, as I suspect it’s more trouble than it’s worth.

The key elements are:
– A ‘make’ script.
– Flycheck
– CSharp-mode.el
– Omnisharp-emacs

The make script

My make script is something I hacked together in python a couple of years ago, worked out from examining the compile commands Monodevelop emits, and it has grown to support various features. You can download it from here – be warned it’s not particularly pretty, and flits between about 3 different coding standards…

Drop it into the root of your Unity project and change it so it captures your source files. The arguments the make script expects are ‘fast/slow’ (explained in a moment), the working directory of the compile (usually the root of the Unity project) and, optionally, a file to exclude and a file to add (again, explained below).

The ‘fast/slow’ bit is something I added to allow for faster compiles. Once you’ve got a Unity project that includes lots of generic game libraries (your own utility functions, NGui, etc) compile times can slow down quite a bit. It therefore makes sense to partition your code into ‘stuff that’s rarely changed’ and ‘actual game stuff’, and only compile the rarely changed stuff… well, rarely.

If you look at the code of the make file there’s one function to get the source files for the rarely change library, and another function to get the source for the main game. If you pass down the ‘fast’ option, it the make script won’t bother compiling the library; if you pass down ‘slow’ it will. In this way it’s possible to keep your general compile times down in Unity to a couple of seconds, even for large projects.

Triggering a compile from within Emacs is fairly straightforward.

(defun unity-compile-game ()
(interactive)
(let ((cmd (concat "python " (project:project-root project:active-project) "make.py fast " (project:project-root project:active-project))))
(compile cmd)))

(defun unity-recompile-game ()
(interactive)
(let ((cmd (concat “python ” (project:project-root project:active-project) “make.py slow ” (project:project-root project:active-project))))
(compile cmd)))

As you can see, all it does is create a command line of ‘python my-project-root/make.py my-project-root slow’ (or ‘fast’) and passes that down to the built in ‘compile’ command. I’ve got a custom project system I use in Emacs (I should probably move over to using something like projectile in the future) which I use to find the project root from a given source file – you should switch in whatever system you’re using instead.

FlyCheck

I use the excellent flycheck to check my builds as I work. Because we’re able to keep our compile times nice and short it provides very fast feedback on code issues as you type, which I personally find invaluable. And, unlike alternatives like Monodevelop’s built in live ‘syntax checking’, it runs an actual build of the game so it’s always completely accurate.

Setting it up is fairly simple:
(require 'flycheck)
(flycheck-define-checker csharp-unity
"Custom checker for Unity projects"
:modes (csharp-mode)
:command ("python" (eval (concat (project:active-project-root) "make.py")) "fast" (eval (project:active-project-root)) source-original source)
:error-patterns((warning line-start (file-name) "(" line (zero-or-more not-newline) "): " (message) line-end)
(error line-start (file-name) "(" line (zero-or-more not-newline) "): " (message) line-end)))

Again, switch in whatever you need to get the root of your project. Flycheck works by saving out a temporary version of the file being edited, which can then be switched for the original file in the build – this is what the ‘exclude’ and ‘extra’ parameters of the make script are for. Thus the command line looks something “python SomeFolder/make.py SomeFolder SomeSourceFile.cs SomeSourceFileFlycheck.cs”.

I should add that the error patterns I’ve given to flycheck are probably monodevelop specific and have only been tested on my OSX machines.

CSharp-mode

CSharp-mode provides C# syntax highlighting and other things for Emacs. It looks like it has been abandoned, but it’s complete enough that I’ve not hit any major issues with it.

I tend turn off csharp-mode’s (dodgy) imenu support and turn on it’s brace matching:

(setq csharp-want-imenu nil)
(local-set-key (kbd "{") 'csharp-insert-open-brace)

Omnisharp for Emacs
Completing the picture is Omnisharp for Emacs, which I’ve blogged about before. It’s awesome, and gives you auto-completion, refactoring and all sorts of other loveliness. I personally use it in tandem with company-mode.

Oh, one more thing. Get annoyed on OSX because double clicking a file in Unity doesn’t open it at the right line number in Emacs? Some smart chap worked out how OSX handles line numbers (the command line isn’t good enough for some operating systems, apparently) and wrote Sublime Proxy to intercept the appropriate events. I’ve (badly) hacked it to work with Emacs. Get it here and make it less bad if you want.

That’s it for now. I hope someone finds it useful, let me know if something doesn’t work.

Simon

* Okay, not really that often.

Advertisements

4 thoughts on “Compiling Unity projects from within Emacs

  1. Fernando Zapata

    Simon, love your Emacs articles, have you considered providing a zipped copy of your .emacs.d directory? I’m in the process of updating my emacs config after several years of neglect and it would help speed up the process 🙂 I bet I’m not alone! Thanks for the valuable info!

    Reply
  2. bbbscarter Post author

    Hi Fernando, thanks!

    A couple of people have pointed out that I should really stick my emacs configs on github or something similar, and it’s certainly something I’m going to consider. Like most people’s, however, it’s all a bit of a mess and needs some attention before anyone else sees it. I’ll try and put some time aside to tidy it up, with a view to releasing it, and I’ll let you know when that happens.

    Thanks!

    Simon

    Reply
  3. James Fulop

    To start using this with Projectile:

    start a projectile project by placing a file named .projectile in your Unity project folder and navigate to that directory once in emacs. After that if you use the command projectile-switch-project your project’s directory should show up

    Switch his unity-recompile-game function in your .emacs file to

    (defun unity-recompile-game ()
    (interactive)
    (compile (eval (concat “python ” (projectile-project-root) “make.py fast ” (projectile-project-root)))))

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s