diff --git a/.gitignore b/.gitignore
index 57c162b89e..989c2222e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
 .gdb_history
-build
+/build
 *~
 .classpath
 .project
@@ -13,3 +13,5 @@ bin
 /*.sublime-*
 workspace/
 src/.cproject
+/cmake-build
+/cmake-build
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000000..785d7a3705
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,28 @@
+# NOTE that this CMake file doesn't current build all of avian.
+# It only builds what's required for example/kaleidoscope.
+
+cmake_minimum_required (VERSION 2.6)
+project (avian)
+
+include_directories (include src)
+
+add_definitions (
+  -DAVIAN_TARGET_FORMAT=AVIAN_FORMAT_MACHO
+
+  -DAVIAN_TARGET_ARCH=AVIAN_ARCH_X86_64
+
+  -DTARGET_BYTES_PER_WORD=8
+  -D__STDC_LIMIT_MACROS
+  -D__STDC_CONSTANT_MACROS
+)
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -fno-exceptions")
+
+include ("cmake/Platform.cmake")
+include (CTest)
+
+# Sadly, we can't use the 'test' target, as that's coopted by ctest
+add_custom_target(check ${CMAKE_CTEST_COMMAND} -V)
+
+add_subdirectory (src)
+add_subdirectory (unittest)
diff --git a/cmake/Platform.cmake b/cmake/Platform.cmake
new file mode 100644
index 0000000000..85470acdeb
--- /dev/null
+++ b/cmake/Platform.cmake
@@ -0,0 +1,8 @@
+IF (APPLE)
+   INCLUDE_DIRECTORIES ( /Developer/Headers/FlatCarbon )
+   FIND_LIBRARY(CORE_FOUNDATION_LIBRARY CoreFoundation)
+
+   MARK_AS_ADVANCED (CORE_FOUNDATION_LIBRARY)
+
+   SET(PLATFORM_LIBS ${CORE_FOUNDATION_LIBRARY})
+ENDIF()
diff --git a/makefile b/makefile
index 77b3fcaf96..e9e2076440 100755
--- a/makefile
+++ b/makefile
@@ -1134,8 +1134,7 @@ vm-depends := $(generated-code) \
 
 vm-sources = \
 	$(src)/system/$(system).cpp \
-	$(src)/system/$(system)/signal.cpp \
-	$(src)/system/$(system)/memory.cpp \
+	$(wildcard $(src)/system/$(system)/*.cpp) \
 	$(src)/finder.cpp \
 	$(src)/machine.cpp \
 	$(src)/util.cpp \
@@ -1270,8 +1269,7 @@ generator-depends := $(wildcard $(src)/*.h)
 generator-sources = \
 	$(src)/tools/type-generator/main.cpp \
 	$(src)/system/$(build-system).cpp \
-	$(src)/system/$(build-system)/signal.cpp \
-	$(src)/system/$(build-system)/memory.cpp \
+	$(wildcard $(src)/system/$(build-system)/*.cpp) \
 	$(src)/finder.cpp \
 	$(src)/util/arg-parser.cpp
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000000..c949643c0a
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,7 @@
+add_subdirectory(codegen)
+add_subdirectory(system)
+add_subdirectory(heap)
+add_subdirectory(util)
+add_subdirectory(tools)
+
+add_library(avian_jvm_finder finder.cpp)
\ No newline at end of file
diff --git a/src/codegen/CMakeLists.txt b/src/codegen/CMakeLists.txt
new file mode 100644
index 0000000000..8a0b4baaf2
--- /dev/null
+++ b/src/codegen/CMakeLists.txt
@@ -0,0 +1,19 @@
+add_library (avian_codegen
+  compiler.cpp
+  registers.cpp
+  runtime.cpp
+  targets.cpp
+
+  compiler/context.cpp
+  compiler/event.cpp
+  compiler/frame.cpp
+  compiler/ir.cpp
+  compiler/promise.cpp
+  compiler/read.cpp
+  compiler/regalloc.cpp
+  compiler/resource.cpp
+  compiler/site.cpp
+  compiler/value.cpp
+)
+
+add_subdirectory(target)
diff --git a/src/codegen/target/CMakeLists.txt b/src/codegen/target/CMakeLists.txt
new file mode 100644
index 0000000000..55edb6b112
--- /dev/null
+++ b/src/codegen/target/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(arm)
+add_subdirectory(x86)
diff --git a/src/codegen/target/arm/CMakeLists.txt b/src/codegen/target/arm/CMakeLists.txt
new file mode 100644
index 0000000000..bc26352adc
--- /dev/null
+++ b/src/codegen/target/arm/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_library(avian_codegen_arm
+  assembler.cpp
+  block.cpp
+  context.cpp
+  fixup.cpp
+  multimethod.cpp
+  operations.cpp
+)
diff --git a/src/codegen/target/x86/CMakeLists.txt b/src/codegen/target/x86/CMakeLists.txt
new file mode 100644
index 0000000000..eb5d5b1554
--- /dev/null
+++ b/src/codegen/target/x86/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_library(avian_codegen_x86
+  assembler.cpp
+  block.cpp
+  context.cpp
+  detect.cpp
+  encode.cpp
+  fixup.cpp
+  multimethod.cpp
+  operations.cpp
+  padding.cpp
+)
diff --git a/src/heap/CMakeLists.txt b/src/heap/CMakeLists.txt
new file mode 100644
index 0000000000..5c6ae4177d
--- /dev/null
+++ b/src/heap/CMakeLists.txt
@@ -0,0 +1,2 @@
+
+add_library(avian_heap heap.cpp)
\ No newline at end of file
diff --git a/src/system/CMakeLists.txt b/src/system/CMakeLists.txt
new file mode 100644
index 0000000000..566ac8100f
--- /dev/null
+++ b/src/system/CMakeLists.txt
@@ -0,0 +1,3 @@
+
+# TODO: use posix.cpp or windows.cpp, depending on platform
+add_library(avian_system posix.cpp posix/crash.cpp)
diff --git a/src/system/posix/crash.cpp b/src/system/posix/crash.cpp
new file mode 100644
index 0000000000..e92a7c726e
--- /dev/null
+++ b/src/system/posix/crash.cpp
@@ -0,0 +1,22 @@
+/* Copyright (c) 2008-2014, Avian Contributors
+
+   Permission to use, copy, modify, and/or distribute this software
+   for any purpose with or without fee is hereby granted, provided
+   that the above copyright notice and this permission notice appear
+   in all copies.
+
+   There is NO WARRANTY for this software.  See license.txt for
+   details. */
+
+#include <avian/common.h>
+
+namespace avian {
+namespace system {
+
+NO_RETURN void crash()
+{
+  abort();
+}
+
+}  // namespace system
+}  // namespace avian
diff --git a/src/system/posix/signal.cpp b/src/system/posix/signal.cpp
index 257808ff87..9b2f015790 100644
--- a/src/system/posix/signal.cpp
+++ b/src/system/posix/signal.cpp
@@ -149,11 +149,6 @@ void handleSignal(int signal, siginfo_t*, void* context)
 
 }  // namespace posix
 
-NO_RETURN void crash()
-{
-  abort();
-}
-
 SignalRegistrar::SignalRegistrar()
 {
   data = new (malloc(sizeof(Data))) Data();
diff --git a/src/system/windows/crash.cpp b/src/system/windows/crash.cpp
new file mode 100644
index 0000000000..032e2d30c2
--- /dev/null
+++ b/src/system/windows/crash.cpp
@@ -0,0 +1,28 @@
+/* Copyright (c) 2008-2014, Avian Contributors
+
+   Permission to use, copy, modify, and/or distribute this software
+   for any purpose with or without fee is hereby granted, provided
+   that the above copyright notice and this permission notice appear
+   in all copies.
+
+   There is NO WARRANTY for this software.  See license.txt for
+   details. */
+
+#include <avian/common.h>
+
+namespace avian {
+namespace system {
+
+NO_RETURN void crash()
+{
+  // trigger an EXCEPTION_ACCESS_VIOLATION, which we will catch and
+  // generate a debug dump for
+  *static_cast<volatile int*>(0) = 0;
+
+  // Some (all?) compilers don't realize that we can't possibly continue past
+  // the above statement.
+  abort();
+}
+
+}  // namespace system
+}  // namespace avian
diff --git a/src/system/windows/signal.cpp b/src/system/windows/signal.cpp
index 34cd5e27a8..d630150a8a 100644
--- a/src/system/windows/signal.cpp
+++ b/src/system/windows/signal.cpp
@@ -324,17 +324,6 @@ bool SignalRegistrar::Data::registerHandler(Handler* handler, int index)
   }
 }
 
-NO_RETURN void crash()
-{
-  // trigger an EXCEPTION_ACCESS_VIOLATION, which we will catch and
-  // generate a debug dump for
-  *static_cast<volatile int*>(0) = 0;
-
-  // Some (all?) compilers don't realize that we can't possibly continue past
-  // the above statement.
-  abort();
-}
-
 bool SignalRegistrar::registerHandler(Signal signal, Handler* handler)
 {
   return data->registerHandler(handler, signal);
diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt
new file mode 100644
index 0000000000..9825129a2c
--- /dev/null
+++ b/src/tools/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_subdirectory(binary-to-object)
+add_subdirectory(object-writer)
+add_subdirectory(type-generator)
diff --git a/src/tools/binary-to-object/CMakeLists.txt b/src/tools/binary-to-object/CMakeLists.txt
new file mode 100644
index 0000000000..1de4e5f9e4
--- /dev/null
+++ b/src/tools/binary-to-object/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_executable(binary_to_object main.cpp)
+
+target_link_libraries(binary_to_object object_writer)
diff --git a/src/tools/object-writer/CMakeLists.txt b/src/tools/object-writer/CMakeLists.txt
new file mode 100644
index 0000000000..1437bf1d67
--- /dev/null
+++ b/src/tools/object-writer/CMakeLists.txt
@@ -0,0 +1,6 @@
+add_library(object_writer
+  elf.cpp
+  mach-o.cpp
+  pe.cpp
+  tools.cpp
+)
\ No newline at end of file
diff --git a/src/tools/type-generator/CMakeLists.txt b/src/tools/type-generator/CMakeLists.txt
new file mode 100644
index 0000000000..1c4b64382e
--- /dev/null
+++ b/src/tools/type-generator/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_executable(type_generator main.cpp)
+
+target_link_libraries(type_generator
+  avian_jvm_finder
+  avian_system
+  avian_util
+  z
+  pthread
+  dl
+  ${PLATFORM_LIBS}
+)
diff --git a/src/tools/type-generator/main.cpp b/src/tools/type-generator/main.cpp
index ccb2464650..68bdf93227 100644
--- a/src/tools/type-generator/main.cpp
+++ b/src/tools/type-generator/main.cpp
@@ -622,7 +622,7 @@ const char* fieldType(const char* spec)
 
 void parseJavaClass(Module& module, ClassParser& clparser, Stream* s)
 {
-  uint32_t magic = s->read4();
+  uint32_t magic UNUSED = s->read4();
   assert(magic == 0xCAFEBABE);
   s->read2();  // minor version
   s->read2();  // major version
diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt
new file mode 100644
index 0000000000..d4311a7b7f
--- /dev/null
+++ b/src/util/CMakeLists.txt
@@ -0,0 +1 @@
+add_library(avian_util arg-parser.cpp fixed-allocator.cpp)
\ No newline at end of file
diff --git a/test/ci.sh b/test/ci.sh
index e5f1edcfaf..11ec7352db 100755
--- a/test/ci.sh
+++ b/test/ci.sh
@@ -2,16 +2,32 @@
 
 set -e
 
+root_dir=$(pwd)
+
 run() {
   echo '==============================================='
+  if [ ! $(pwd) = ${root_dir} ]; then
+    printf "cd $(pwd); "
+  fi
   echo "${@}"
   echo '==============================================='
   "${@}"
 }
 
+run_cmake() {
+  mkdir -p cmake-build
+  rm -rf cmake-build/*
+  cd  cmake-build
+  run cmake ${@} ..
+  run make -j4 check
+  cd ..
+}
+
 if [ ${#} -gt 0 ]; then
   run make ${@}
 else
+  run_cmake -DCMAKE_BUILD_TYPE=Debug
+
   run make jdk-test
   run make test
   run make mode=debug test
diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt
new file mode 100644
index 0000000000..0f4520d162
--- /dev/null
+++ b/unittest/CMakeLists.txt
@@ -0,0 +1,25 @@
+
+include_directories (${CMAKE_CURRENT_SOURCE_DIR})
+
+add_executable (avian_unittest
+  test-harness.cpp
+
+  codegen/assembler-test.cpp
+  codegen/registers-test.cpp
+
+  util/arg-parser-test.cpp
+)
+
+target_link_libraries (avian_unittest
+  avian_codegen
+  avian_codegen_x86
+  avian_system
+  avian_heap
+  avian_util
+  pthread
+  dl
+  ${PLATFORM_LIBS}
+)
+
+add_test(NAME avian_unittest COMMAND avian_unittest)
+add_dependencies(check avian_unittest)