[gnome-builder/wip/libide] libide: add libide design and prototype



commit 4ff8b714b2c8d881383aaf477ee6dd0b1c3024c1
Author: Christian Hergert <christian hergert me>
Date:   Sat Feb 7 12:02:23 2015 -0800

    libide: add libide design and prototype
    
                ** This is not ready for public consumption **
    
    This includes the basic design and skeleton implementation of plans for
    libide. Everything extends from the IdeContext object. All objects, with
    the exception of IdeContext, are decendants of an IdeContext.
    
    Since scripting is important for this system, it is important that objects
    are GObject Introspectable. We will add this in the not too distant
    future.
    
    For more information on the objects and components of libide, see
    libide/DESIGN.md.
    
    If you would like to contribute to libide development, get in touch via
    email or IRC so we can collaborate on which component you would like to
    implement.
    
    -- Christian

 .gitignore                                         |    1 +
 Makefile.am                                        |    1 +
 build/autotools/autoconf.d/50_dependencies.post-am |    3 +
 libide/DESIGN.md                                   |  347 +++++++++
 libide/Makefile.am                                 |   93 +++
 libide/autotools/ide-autotools-build-system.c      |  396 ++++++++++
 libide/autotools/ide-autotools-build-system.h      |   40 +
 libide/autotools/ide-autotools-build-task.c        |  789 ++++++++++++++++++++
 libide/autotools/ide-autotools-build-task.h        |   49 ++
 libide/autotools/ide-autotools-builder.c           |  298 ++++++++
 libide/autotools/ide-autotools-builder.h           |   38 +
 libide/directory/ide-directory-build-system.c      |  141 ++++
 libide/directory/ide-directory-build-system.h      |   38 +
 libide/directory/ide-directory-vcs.c               |  168 +++++
 libide/directory/ide-directory-vcs.h               |   38 +
 libide/gconstructor.h                              |   94 +++
 libide/git/ide-git-vcs.c                           |  316 ++++++++
 libide/git/ide-git-vcs.h                           |   41 +
 libide/ide-async-helper.c                          |   85 +++
 libide/ide-async-helper.h                          |   40 +
 libide/ide-buffer-iter.h                           |   40 +
 libide/ide-buffer.h                                |   40 +
 libide/ide-build-result.c                          |  336 +++++++++
 libide/ide-build-result.h                          |   51 ++
 libide/ide-build-system.c                          |  240 ++++++
 libide/ide-build-system.h                          |   58 ++
 libide/ide-builder.c                               |  145 ++++
 libide/ide-builder.h                               |   55 ++
 libide/ide-context.c                               |  775 +++++++++++++++++++
 libide/ide-context.h                               |   58 ++
 libide/ide-debugger.h                              |   40 +
 libide/ide-deployer.h                              |   37 +
 libide/ide-device-manager.c                        |  297 ++++++++
 libide/ide-device-manager.h                        |   46 ++
 libide/ide-device-provider.c                       |  187 +++++
 libide/ide-device-provider.h                       |   53 ++
 libide/ide-device.c                                |  263 +++++++
 libide/ide-device.h                                |   49 ++
 libide/ide-diagnostic-provider.h                   |   40 +
 libide/ide-diagnostic.h                            |   40 +
 libide/ide-executable.h                            |   40 +
 libide/ide-executer.h                              |   40 +
 libide/ide-file.c                                  |  138 ++++
 libide/ide-file.h                                  |   37 +
 libide/ide-global.h                                |   31 +
 libide/ide-indenter.h                              |   40 +
 libide/ide-language.h                              |   40 +
 libide/ide-object.c                                |  311 ++++++++
 libide/ide-object.h                                |   50 ++
 libide/ide-process.h                               |   40 +
 libide/ide-project-file.c                          |  145 ++++
 libide/ide-project-file.h                          |   44 ++
 libide/ide-project-files.c                         |   31 +
 libide/ide-project-files.h                         |   38 +
 libide/ide-project-item.c                          |  176 +++++
 libide/ide-project-item.h                          |   42 +
 libide/ide-project.c                               |  193 +++++
 libide/ide-project.h                               |   42 +
 libide/ide-refactory.h                             |   40 +
 libide/ide-script.c                                |   78 ++
 libide/ide-script.h                                |   43 ++
 libide/ide-search-engine.h                         |   37 +
 libide/ide-search-provider.h                       |   37 +
 libide/ide-search-result.h                         |   37 +
 libide/ide-service.c                               |  144 ++++
 libide/ide-service.h                               |   46 ++
 libide/ide-symbol-resolver.h                       |   40 +
 libide/ide-symbol.h                                |   37 +
 libide/ide-target.h                                |   40 +
 libide/ide-test-case.h                             |   40 +
 libide/ide-test-suite.h                            |   40 +
 libide/ide-types.h                                 |  127 ++++
 libide/ide-unsaved-files.c                         |  474 ++++++++++++
 libide/ide-unsaved-files.h                         |   57 ++
 libide/ide-vcs.c                                   |   60 ++
 libide/ide-vcs.h                                   |   48 ++
 libide/ide.c                                       |   90 +++
 libide/ide.h                                       |   78 ++
 libide/local/ide-local-device.c                    |  117 +++
 libide/local/ide-local-device.h                    |   38 +
 libide/tasks/ide-load-directory-task.c             |  295 ++++++++
 libide/tasks/ide-load-directory-task.h             |   38 +
 82 files changed, 9425 insertions(+), 0 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 0743e0f..db57b86 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@ intltool-extract.in
 intltool-merge.in
 intltool-update.in
 libgnome-builder.la
+libide.la
 libtool
 stamp-h1
 test-c-parse-helper
diff --git a/Makefile.am b/Makefile.am
index b5eeb91..9661af0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5,6 +5,7 @@ include build/autotools/automake/AutomakeDocs.mk
 
 ACLOCAL_AMFLAGS = -I build/autotools/m4 ${ACLOCAL_FLAGS}
 
+include libide/Makefile.am
 include src/gnome-builder.mk
 include data/data.mk
 include data/gsettings.mk
diff --git a/build/autotools/autoconf.d/50_dependencies.post-am 
b/build/autotools/autoconf.d/50_dependencies.post-am
index 7602429..0b336bb 100644
--- a/build/autotools/autoconf.d/50_dependencies.post-am
+++ b/build/autotools/autoconf.d/50_dependencies.post-am
@@ -2,4 +2,7 @@ PKG_CHECK_MODULES(BUILDER, [gtk+-3.0 >= 3.15.3
                             gtksourceview-3.0 >= 3.15.3
                             libdevhelp-3.0 >= 3.14.0
                             libgit2-glib-1.0 >= 0.0.24])
+PKG_CHECK_MODULES(LIBIDE, [gio-2.0 >= 2.43.4
+                           gio-unix-2.0 >= 2.43.4
+                           libgit2-glib-1.0 >= 0.0.24])
 
diff --git a/libide/DESIGN.md b/libide/DESIGN.md
new file mode 100644
index 0000000..7652586
--- /dev/null
+++ b/libide/DESIGN.md
@@ -0,0 +1,347 @@
+# LibIDE Design
+
+LibIDE is a library that abstracts typical IDE operations.  It is meant to be
+the base library behind Builder.  By separating this into its own static
+library, we make things easier to unit test.  Or at least, that is the hope.
+
+## Some Open Questions
+
+ - How do we plumb editor settings in proper order?
+   - Global Settings
+   - Project Settings
+   - Per-file modelines
+ - How do we manage assets such as icons, settings, desktop and service files.
+ - Does it make sense to add/remove/modify translations?
+
+## Objects
+
+### IdeContext
+
+`IdeContext` is the base object you will deal with in LibIDE. Think of it as a
+library handle. Everything stems from an `IdeContext`.
+
+Any object that is part of the `IdeContext` can register commands into the
+context. The context has services, a project tree, build system, version
+control system, unsaved files, search engine, and more.
+
+In Builder, a context would be attached to a GbWorkbench.
+
+### IdeBuildSystem
+
+The `IdeBuildSystem` is responsible for loading projects and writing changes to the project back to disk.
+It also registers commands that can be executed to perform transforms on the build system.
+Such transforms might include adding a file.
+
+### IdeProject
+
+An `IdeProject` represents the project on disk.  Various subsystems collaborate
+to build the project tree.  Files might come from the VCS. Build system targets
+from the `IdeBuildSystem`.  Class names might come from various symbol
+resolvers.  GtkWidget subclasses (for browsing widgets rather than files) might
+come from another service.
+
+The build system should update the tree when it performs transforms on the
+underlying build system.  Such changes might include adding a file or target.
+
+We need a good way to listen for changes on the tree so that UI can update
+iteself.
+
+### IdeProjectItem
+
+The `IdeProjectItem` represents an item in the `IdeProject` tree.  What the
+project item contains is backend specific.  IDEs will need to know a bit about
+the backend to render information in the IDE appropriately.
+
+There will be some known subclasses that IDEs can traverse to find information
+they want to know about.  Files in the project could be one (and loaded by the
+VCS layer).  Targets could be another (and loaded by the build system).
+
+### IdeLanguage
+
+`IdeLanguage` represents a programming language, such as C, C++, or Python.  It
+has some general utilities associated with it that can be used in IDEs.  One
+such example is commenting a block of text.  `IdeLanguage` can be used to
+retrieve an `IdeSymbolResolver`, `IdeRefactory`, and others.
+
+### IdeFile
+
+An `IdeFile` is an abstraction of a file within the project.  It is partly a
+convenience object so that we can map a file to a language as well as list
+files in the VCS.  `IdeFile` also knows how to load a file from the VCS
+backend.
+
+### IdeBuffer and IdeBufferIter
+
+`IdeBuffer` is an interface for passing around buffers that dont require
+copying all of the text out of GtkTextBuffer or GtkTextIter slices.  It also
+allows command handlers to work with a higher level structure than just a
+string buffer.
+
+### IdeIndenter
+
+`IdeIndenter` is a class that can help you perform auto indentation in your
+IDE.  Based on the cursor position in the file, it can suggest what the
+indentation should be when various trigger keys are pressed.
+
+### IdeRefactory
+
+`IdeRefactory` represents a refactoring engine.  This can be retrieved for a
+given language, and the language can be retrieved from a given file.  The
+refactory can expose available commands that can be executed.  Such an example
+might be a "Extract Method" command.  Most of these will need to be programmed
+into the IDE in a non-generic way.
+
+### IdeVcs
+
+An `IdeVcs` represents a version control system.  The version control system
+can perform basic operations like creating a branch, or snapshoting the
+repository.  You can also add and remove files from the VCS, which should be
+performed automatically when performing certain project transforms.
+
+### IdeUnsavedFiles
+
+The `IdeUnsavedFiles` object represents the collection of open buffers in the
+IDE.  Many services such as Clang need access to these when determining code
+completion and diagnostics.
+
+The `IdeUnsavedFiles` class also abstracts the saving of modified buffers to
+the drafts directory.  This allows us to maintain modified buffer state when
+the user closes the window.
+
+### IdeSearchEngine
+
+The `IdeSearchEngine` manages search within the IDE.  It can have various
+search providers that can resolve information.  Some search providers might
+even store mined search information on disk for fast future lookup.  Having the
+search engine live in LibIDE allows it fast access to project information,
+files, and refactory information like symbols.
+
+### IdeSearchProvider
+
+The `IdeSearchProvider` provides search results to the IdeSearchEngine.
+
+### IdeSearchResult
+
+The `IdeSearchResult` represents a search result.  It can be activated to jump
+to a particular file or URI, line number, or other context specific
+information.  IDEs will need to know about search result types to properly
+route to the target information.
+
+### IdeService
+
+`IdeService` is a base service that lives inside an `IdeContext`.
+These are used to implement singleton like features that arent quite singletons.
+They are per-context singletons.
+
+This might be used by something such as a clang indexer to ensure only one
+CXIndex exists per `IdeContext`.
+
+### IdeDiagnoser
+
+`IdeDiagnoser` provides access to diagnostics for a given file.  It will take
+the unsaved file state into account via the `IdeUnsavedFiles` attached to the
+`IdeContext`.
+
+The `IdeDiagnoser` instance is retrieved from the `IdeLanguage` of the
+`IdeFile`.  The `IdeFile` is passed to the `IdeDiagnoser` which will query the
+`IdeClangService`.  The `IdeClangService` will use the unsaved file state in
+`IdeUnsavedFiles` to pass state to the clang API.  Obviously, non-C languages
+will have a service for their features as well (python, gjs, etc).
+
+The `IdeDiagnoser` for C will be somewhat dependent on a build system since it
+needs access to CFLAGS when communicating with Clang.
+
+### IdeScript
+
+An `IdeScript` is a user defined script that can run in the `IdeContext`.  It
+is loaded using a language such as JavaScript to register new commands or tweak
+settings as needed in the context.  For example,
+~/.config/gnome-builder/scripts/*.js might be loaded into the context using
+`IdeScript` to register custom commands.
+
+### IdeDevice, IdeDeviceProvider, and IdeDeviceManager
+
+`IdeDevice` represents a device that can run software.
+
+- It might be your local system, or even an xdg-app runtime on your local
+  system.
+- It might be an externally connected tablet or server.
+- It might be a simulator in an application like Boxes.
+- It might be a remote desktop, possibly alternate operating system.
+
+The `IdeDeviceProvider` is responsible for discovering devices during device
+settling.  Some devices might support connecting over TCP or Wi-Fi.
+
+### IdeDebugger
+
+Fetched via an `IdeDevice`.  Not all devices will support all debuggers.
+Therefore we need to pass an IdeTarget to the device so it can determine if it
+supports it.  pdb for python for example.
+
+### IdeSymbolResolver
+
+This is used to list symbols in a document, find a symbol by name, and fetch a
+symbol at a give position in a file.  It should take the contexts unsaved files
+into account.  A symbol resolver for C might access the same clang service that
+is used for diagnostics, sharing their translation units.
+
+### IdeDeployer
+
+This is used to deploy a project to a device.  The local device is simpler in
+that it is mostly just a `make install`.
+
+Remove devices may be more complicated.  This will depend on both the build
+system and target device to work properly.
+
+Not all combinations will be supportable.
+
+### IdeExecuter
+
+This represents a strategy for executing a target on a particular device.
+This is accessed from the device by passing the particular target.
+
+### IdeProcess
+
+This represents a subprocess that is executing a target. It could be local or
+on a remote device. This is created by calling `ide_executer_execute()`.
+
+### IdeTestSuite, IdeTestCase
+
+Can be fetched from an `IdeBuildSystem`. They should be able to provide an
+`IdeExecutable` that can be used to get an `IdeExecuter` by the `IdeDevice`.
+
+
+
+
+## Questions and Answers
+
+### How do I add a GSetting to my application?
+
+After the IDE presents appropriate UI to the user, the IDE would activate the
+"build-system.gsetting.add" command. Not all build systems may implement this
+command, so it is important that the IDE check that the command is available
+using `ide_context_has_command()`. You can also connect to the `::command-added`
+and `::command-removed` signals which contain detailed quarks for the command
+in question. This should make showing proper UI or enabling/disabling GActions
+easier.
+
+### How do I add a new executable target to my application?
+
+Create a new IdeTarget subclass based with the information you desire.
+You can check that the build system supports the target with
+`ide_build_system_supports_target_type()` and providing the `GType`.
+Then call `ide_build_system_add_target()`.
+
+### How do I add a new C file to my shared library?
+
+Create a new `IdeFile` using the parameters you want.
+Then use `ide_target_add_file()` to add a file to a target, or another
+interface method based on where you want to add the file.
+`ide_build_system_add_file()` could be used to add the file starting at the
+toplevel.
+
+### What happens if I try to add a .vala file to a C-based shared library?
+
+This should be okay if using autotools. So I imagine that based on the target,
+it would accept or reject the item.
+
+### How do I perform a trigger whenever a file is saved in the IDE?
+
+In an IDE script, connect to the "file-saved" signal to perform an action.
+The actual save is performed in the default handler, so mutating the content
+can be done by hooking this signal. Using `G_CONNECT_AFTER` when connecting to
+the signal will result in being called after the save has occurred.
+
+`ide_context_emit_save_file()` and `ide_context_emit_file_saved()` should be
+called by the IDE to trigger any scripts that desire handling the feature.
+
+```
+Context.connect('save-file', function(file, buffer) {
+    data.replace('\r\n','\n');
+});
+
+Context.connect('file-saved', function(file, buffer) {
+    console.log('file saved ' + file.get_name() + '\n');
+});
+```
+
+### How do I get the list of targets found in a project?
+
+`ide_build_system_list_targets()`
+
+### How would a golang project be loaded?
+
+Golang uses a particular directory layout, so a GolangBuildSystem would need to
+be implemented. It would implement various commands for looking up targets and
+building the project. However, no actual project file is used.
+
+### How can we execute a target without a debugger?
+
+```c
+executer = ide_device_get_executer (device, target);
+process = ide_executer_execute (executer);
+```
+
+### How would a user execute a particular, or all, test suites in a project.
+
+Some build systems might implement an interface method to retrieve a list of
+`IdeTestCase` or `IdeTestSuite`. It should then be possible to get an
+`IdeExecutable` instance for the test that will result in the test being
+executed. In some cases, this might run the test via a helper program such
+as python.
+
+### How would a user begin debugging a particular target executable on a remote device?
+
+First we get a handle to the `IdeDebugger` for the `IdeDevice`.
+We may need to specify something like an `IdeTarget` to be able to do so.
+
+### How would a user get a list of symbols found in the current file?
+
+First, the IDE would retrieve an `IdeSymbolResolver` for the given `IdeFile`.
+Something like `ide_file_get_symbol_resolver()` seems like an obvious way to
+go. Possibly via the IdeFile language provides a cleaner abstraction.
+
+Then, use the resolvers interface methods to retrieve the list of symbols
+within the file.
+
+This does bring up the question of how should we load an IdeSymbolResolver for
+a particular file. It might rely on both the `IdeBuildSystem` and the
+`IdeLanguage` of the file.
+
+Let's discuss the example of C. If the resolver for C can retrieve the CFLAGS
+for a file using the `IdeBuildSystem`, it need only query the `IdeClangService`
+to retrieve the list of symbols using the tranlsation unit used with the file.
+This means a concrete `IdeCSymbolResolver` concrete implementation.
+
+An implementation for python, such as `IdePythonSymbolResolver` may be able to
+work simply using the contents of the `IdeFile` since python provides an `AST`
+method.
+
+### How would a user get the symbol underneath the current cursor?
+
+Similar to getting the list of symbols, the IDE would get a handle to a resolver
+for the given `IdeFile`. Then it would use the particular method to get the
+symbol underneath the line/column for the cursor.
+
+### How would a user jump to the definition of a symbol?
+
+Using the `IdeSymbolResolver` for the `IdeFile`, the IDE would call the method
+to get the location of a particular symbol by name. Then it can open that file
+using it's file loading subsystem.
+
+### How would a user add a breakpoint to the active debugging process.
+
+First, the IDE would get a handle to a debugger instance for the current
+execution device. This would likely be performed via the `IdeDevice` that is
+selected for execution. `ide_device_get_debugger()` passing the execution
+target to be executed. Then the device implementation can determine if it has
+a debugger suitable for that execution target.
+
+Local might support pdb in addition to gdb. However, a tablet/phone might not.
+
+### How would a user apply a fixit to a file?
+
+First we access the the `IdeDiagnostics` for the `IdeFile`. Then we iterate
+the available diagnostics that are available. Using one of them, the caller
+can replace each file denoted by the diagnostic with the buffer modifications
+provided.
diff --git a/libide/Makefile.am b/libide/Makefile.am
new file mode 100644
index 0000000..1f77f74
--- /dev/null
+++ b/libide/Makefile.am
@@ -0,0 +1,93 @@
+noinst_LTLIBRARIES += libide.la
+
+libide_la_SOURCES = \
+       libide/autotools/ide-autotools-build-system.c \
+       libide/autotools/ide-autotools-build-system.h \
+       libide/autotools/ide-autotools-build-task.c \
+       libide/autotools/ide-autotools-build-task.h \
+       libide/autotools/ide-autotools-builder.c \
+       libide/autotools/ide-autotools-builder.h \
+       libide/directory/ide-directory-build-system.c \
+       libide/directory/ide-directory-build-system.h \
+       libide/directory/ide-directory-vcs.c \
+       libide/directory/ide-directory-vcs.h \
+       libide/gconstructor.h \
+       libide/git/ide-git-vcs.c \
+       libide/git/ide-git-vcs.h \
+       libide/ide-async-helper.c \
+       libide/ide-async-helper.h \
+       libide/ide-buffer-iter.h \
+       libide/ide-buffer.h \
+       libide/ide-build-result.c \
+       libide/ide-build-result.h \
+       libide/ide-build-system.c \
+       libide/ide-build-system.h \
+       libide/ide-builder.c \
+       libide/ide-builder.h \
+       libide/ide-context.c \
+       libide/ide-context.h \
+       libide/ide-debugger.h \
+       libide/ide-deployer.h \
+       libide/ide-device-manager.c \
+       libide/ide-device-manager.h \
+       libide/ide-device-provider.c \
+       libide/ide-device-provider.h \
+       libide/ide-device.c \
+       libide/ide-device.h \
+       libide/ide-diagnostic-provider.h \
+       libide/ide-diagnostic.h \
+       libide/ide-executable.h \
+       libide/ide-executer.h \
+       libide/ide-file.c \
+       libide/ide-file.h \
+       libide/ide-global.h \
+       libide/ide-indenter.h \
+       libide/ide-language.h \
+       libide/ide-object.c \
+       libide/ide-object.h \
+       libide/ide-process.h \
+       libide/ide-project-file.c \
+       libide/ide-project-file.h \
+       libide/ide-project-files.c \
+       libide/ide-project-files.h \
+       libide/ide-project-item.c \
+       libide/ide-project-item.h \
+       libide/ide-project.c \
+       libide/ide-project.h \
+       libide/ide-refactory.h \
+       libide/ide-script.c \
+       libide/ide-script.h \
+       libide/ide-search-engine.h \
+       libide/ide-search-provider.h \
+       libide/ide-search-result.h \
+       libide/ide-service.c \
+       libide/ide-service.h \
+       libide/ide-symbol-resolver.h \
+       libide/ide-symbol.h \
+       libide/ide-target.h \
+       libide/ide-test-case.h \
+       libide/ide-test-suite.h \
+       libide/ide-types.h \
+       libide/ide-unsaved-files.c \
+       libide/ide-unsaved-files.h \
+       libide/ide-vcs.c \
+       libide/ide-vcs.h \
+       libide/ide.c \
+       libide/ide.h \
+       libide/local/ide-local-device.c \
+       libide/local/ide-local-device.h \
+       libide/tasks/ide-load-directory-task.c \
+       libide/tasks/ide-load-directory-task.h \
+       $(NULL)
+
+libide_la_CFLAGS = \
+       -I$(top_srcdir)/libide \
+       -I$(top_srcdir)/libide/autotools \
+       -I$(top_srcdir)/libide/directory \
+       -I$(top_srcdir)/libide/git \
+       -I$(top_srcdir)/libide/local \
+       -I$(top_srcdir)/libide/tasks \
+       $(LIBIDE_CFLAGS)
+
+libide_la_LIBADD = \
+       $(LIBIDE_LIBS)
diff --git a/libide/autotools/ide-autotools-build-system.c b/libide/autotools/ide-autotools-build-system.c
new file mode 100644
index 0000000..c25f6a2
--- /dev/null
+++ b/libide/autotools/ide-autotools-build-system.c
@@ -0,0 +1,396 @@
+/* ide-autotools-build-system.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "ide-autotools-build-system.h"
+#include "ide-context.h"
+#include "ide-device.h"
+
+#include "autotools/ide-autotools-builder.h"
+
+typedef struct
+{
+  gchar *tarball_name;
+} IdeAutotoolsBuildSystemPrivate;
+
+static void async_initable_iface_init (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (IdeAutotoolsBuildSystem,
+                        ide_autotools_build_system,
+                        IDE_TYPE_BUILD_SYSTEM,
+                        0,
+                        G_ADD_PRIVATE (IdeAutotoolsBuildSystem)
+                        G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
+                                               async_initable_iface_init))
+
+enum {
+  PROP_0,
+  PROP_TARBALL_NAME,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+const gchar *
+ide_autotools_build_system_get_tarball_name (IdeAutotoolsBuildSystem *system)
+{
+  IdeAutotoolsBuildSystemPrivate *priv = ide_autotools_build_system_get_instance_private (system);
+
+  g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (system), NULL);
+
+  return priv->tarball_name;
+}
+
+static IdeBuilder *
+ide_autotools_build_system_get_builder (IdeBuildSystem  *system,
+                                        GKeyFile        *config,
+                                        IdeDevice       *device,
+                                        GError         **error)
+{
+  IdeBuilder *ret;
+  IdeContext *context;
+
+  g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (system), NULL);
+  g_return_val_if_fail (config, NULL);
+  g_return_val_if_fail (IDE_IS_DEVICE (device), NULL);
+
+  context = ide_object_get_context (IDE_OBJECT (system));
+
+  ret = g_object_new (IDE_TYPE_AUTOTOOLS_BUILDER,
+                      "context", context,
+                      "config", config,
+                      "device", device,
+                      NULL);
+
+  return ret;
+}
+
+static void
+discover_query_info_cb (GObject      *object,
+                        GAsyncResult *result,
+                        gpointer      user_data)
+{
+  g_autoptr(GFileInfo) file_info = NULL;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GFile) child = NULL;
+  GFile *file = (GFile *)object;
+  GError *error = NULL;
+  GFileType file_type;
+
+  g_return_if_fail (G_IS_FILE (file));
+  g_return_if_fail (G_IS_TASK (task));
+
+  file_info = g_file_query_info_finish (file, result, &error);
+
+  if (!file_info)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  file_type = g_file_info_get_file_type (file_info);
+
+  if (file_type == G_FILE_TYPE_REGULAR)
+    {
+      const gchar *name;
+
+      name = g_file_info_get_name (file_info);
+
+      if ((g_strcmp0 (name, "configure.ac") == 0) ||
+          (g_strcmp0 (name, "configure.in") == 0))
+        {
+          g_task_return_pointer (task, g_object_ref (file), g_object_unref);
+          return;
+        }
+    }
+  else if (file_type != G_FILE_TYPE_DIRECTORY)
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_NOT_SUPPORTED,
+                               _("Not an autotools project file."));
+      return;
+    }
+
+  child = g_file_get_child (file, "configure.ac");
+  g_file_query_info_async (child,
+                           (G_FILE_ATTRIBUTE_STANDARD_TYPE","
+                            G_FILE_ATTRIBUTE_STANDARD_NAME),
+                           G_FILE_QUERY_INFO_NONE,
+                           G_PRIORITY_DEFAULT,
+                           g_task_get_cancellable (task),
+                           discover_query_info_cb,
+                           g_object_ref (task));
+}
+
+static void
+ide_autotools_build_system_discover_file_async (IdeAutotoolsBuildSystem *system,
+                                                GFile                   *file,
+                                                GCancellable            *cancellable,
+                                                GAsyncReadyCallback      callback,
+                                                gpointer                 user_data)
+{
+  g_autoptr(gchar) name = NULL;
+  g_autoptr(GTask) task = NULL;
+
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (system));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (system, cancellable, callback, user_data);
+  name = g_file_get_basename (file);
+
+  if (!name)
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_INVALID_FILENAME,
+                               _("Invalid file provided to discover."));
+      return;
+    }
+
+  if (g_str_equal (name, "configure.ac") || g_str_equal (name, "configure.in"))
+    {
+      g_task_return_pointer (task, g_object_ref (file), g_object_unref);
+      return;
+    }
+
+  g_file_query_info_async (file,
+                           (G_FILE_ATTRIBUTE_STANDARD_TYPE","
+                            G_FILE_ATTRIBUTE_STANDARD_NAME),
+                           G_FILE_QUERY_INFO_NONE,
+                           G_PRIORITY_DEFAULT,
+                           cancellable,
+                           discover_query_info_cb,
+                           g_object_ref (task));
+}
+
+static GFile *
+ide_autotools_build_system_discover_file_finish (IdeAutotoolsBuildSystem  *system,
+                                                 GAsyncResult             *result,
+                                                 GError                  **error)
+{
+  GTask *task = (GTask *)result;
+
+  g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (system), NULL);
+  g_return_val_if_fail (G_IS_TASK (task), NULL);
+
+  return g_task_propagate_pointer (task, error);
+}
+
+static void
+ide_autotools_build_system_finalize (GObject *object)
+{
+  IdeAutotoolsBuildSystemPrivate *priv;
+  IdeAutotoolsBuildSystem *system = (IdeAutotoolsBuildSystem *)object;
+
+  priv = ide_autotools_build_system_get_instance_private (system);
+
+  g_clear_pointer (&priv->tarball_name, g_free);
+
+  G_OBJECT_CLASS (ide_autotools_build_system_parent_class)->finalize (object);
+}
+
+static void
+ide_autotools_build_system_get_property (GObject    *object,
+                                         guint       prop_id,
+                                         GValue     *value,
+                                         GParamSpec *pspec)
+{
+  IdeAutotoolsBuildSystem *self = IDE_AUTOTOOLS_BUILD_SYSTEM (object);
+
+  switch (prop_id)
+    {
+    case PROP_TARBALL_NAME:
+      g_value_set_string (value,
+                          ide_autotools_build_system_get_tarball_name (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_autotools_build_system_set_property (GObject      *object,
+                                         guint         prop_id,
+                                         const GValue *value,
+                                         GParamSpec   *pspec)
+{
+  //IdeAutotoolsBuildSystem *self = IDE_AUTOTOOLS_BUILD_SYSTEM (object);
+
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_autotools_build_system_class_init (IdeAutotoolsBuildSystemClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeBuildSystemClass *build_system_class = IDE_BUILD_SYSTEM_CLASS (klass);
+
+  object_class->finalize = ide_autotools_build_system_finalize;
+  object_class->get_property = ide_autotools_build_system_get_property;
+  object_class->set_property = ide_autotools_build_system_set_property;
+
+  build_system_class->get_builder =   ide_autotools_build_system_get_builder;
+
+  gParamSpecs [PROP_TARBALL_NAME] =
+    g_param_spec_string ("tarball-name",
+                         _("Tarball Name"),
+                         _("The name of the project tarball."),
+                         NULL,
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_TARBALL_NAME,
+                                   gParamSpecs [PROP_TARBALL_NAME]);
+}
+
+static void
+ide_autotools_build_system_init (IdeAutotoolsBuildSystem *self)
+{
+}
+
+static void
+ide_autotools_build_system_parse_async (IdeAutotoolsBuildSystem *system,
+                                        GFile                   *project_file,
+                                        GCancellable            *cancellable,
+                                        GAsyncReadyCallback      callback,
+                                        gpointer                 user_data)
+{
+  g_autoptr(GTask) task = NULL;
+
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (system));
+  g_return_if_fail (G_IS_FILE (project_file));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (system, cancellable, callback, user_data);
+  g_task_return_boolean (task, TRUE);
+}
+
+static gboolean
+ide_autotools_build_system_parse_finish (IdeAutotoolsBuildSystem  *system,
+                                         GAsyncResult             *result,
+                                         GError                  **error)
+{
+  GTask *task = (GTask *)result;
+
+  g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (system), FALSE);
+  g_return_val_if_fail (G_IS_TASK (task), FALSE);
+
+  return g_task_propagate_boolean (task, error);
+}
+
+static void
+parse_cb (GObject      *object,
+          GAsyncResult *result,
+          gpointer      user_data)
+{
+  IdeAutotoolsBuildSystem *self = (IdeAutotoolsBuildSystem *)object;
+  g_autoptr(GTask) task = user_data;
+  GError *error = NULL;
+
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self));
+  g_return_if_fail (G_IS_TASK (task));
+
+  if (!ide_autotools_build_system_parse_finish (self, result, &error))
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  g_task_return_boolean (task, TRUE);
+}
+
+static void
+discover_file_cb (GObject      *object,
+                  GAsyncResult *result,
+                  gpointer      user_data)
+{
+  IdeAutotoolsBuildSystem *self;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GFile) file = NULL;
+  GError *error = NULL;
+
+  g_return_if_fail (G_IS_TASK (task));
+
+  self = g_task_get_source_object (task);
+  file = ide_autotools_build_system_discover_file_finish (self, result, &error);
+
+  if (!file)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  ide_autotools_build_system_parse_async (self,
+                                          file,
+                                          g_task_get_cancellable (task),
+                                          parse_cb,
+                                          g_object_ref (task));
+}
+
+static void
+ide_autotools_build_system_init_async (GAsyncInitable      *initable,
+                                       gint                 io_priority,
+                                       GCancellable        *cancellable,
+                                       GAsyncReadyCallback  callback,
+                                       gpointer             user_data)
+{
+  IdeAutotoolsBuildSystem *system = (IdeAutotoolsBuildSystem *)initable;
+  g_autoptr(GTask) task = NULL;
+  IdeContext *context;
+  GFile *project_file;
+
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (system));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (initable, cancellable, callback, user_data);
+  context = ide_object_get_context (IDE_OBJECT (system));
+  project_file = ide_context_get_project_file (context);
+
+  ide_autotools_build_system_discover_file_async (system,
+                                                  project_file,
+                                                  cancellable,
+                                                  discover_file_cb,
+                                                  g_object_ref (task));
+}
+
+static gboolean
+ide_autotools_build_system_init_finish (GAsyncInitable  *initable,
+                                        GAsyncResult    *result,
+                                        GError         **error)
+{
+  IdeAutotoolsBuildSystem *system = (IdeAutotoolsBuildSystem *)initable;
+  GTask *task = (GTask *)result;
+
+  g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (system), FALSE);
+  g_return_val_if_fail (G_IS_TASK (task), FALSE);
+
+  return g_task_propagate_boolean (task, error);
+}
+
+static void
+async_initable_iface_init (GAsyncInitableIface *iface)
+{
+  iface->init_async = ide_autotools_build_system_init_async;
+  iface->init_finish = ide_autotools_build_system_init_finish;
+}
diff --git a/libide/autotools/ide-autotools-build-system.h b/libide/autotools/ide-autotools-build-system.h
new file mode 100644
index 0000000..42da81b
--- /dev/null
+++ b/libide/autotools/ide-autotools-build-system.h
@@ -0,0 +1,40 @@
+/* ide-autotools-build-system.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_AUTOTOOLS_BUILD_SYSTEM_H
+#define IDE_AUTOTOOLS_BUILD_SYSTEM_H
+
+#include "ide-build-system.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_AUTOTOOLS_BUILD_SYSTEM (ide_autotools_build_system_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeAutotoolsBuildSystem, ide_autotools_build_system,
+                      IDE, AUTOTOOLS_BUILD_SYSTEM, IdeBuildSystem)
+
+struct _IdeAutotoolsBuildSystem
+{
+  IdeBuildSystem parent;
+};
+
+const gchar *ide_autotools_build_system_get_tarball_name (IdeAutotoolsBuildSystem *self);
+
+G_END_DECLS
+
+#endif /* IDE_AUTOTOOLS_BUILD_SYSTEM_H */
diff --git a/libide/autotools/ide-autotools-build-task.c b/libide/autotools/ide-autotools-build-task.c
new file mode 100644
index 0000000..a0e523d
--- /dev/null
+++ b/libide/autotools/ide-autotools-build-task.c
@@ -0,0 +1,789 @@
+/* ide-autotools-build-task.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <fcntl.h>
+#include <glib/gi18n.h>
+#include <unistd.h>
+
+#include "ide-autotools-build-task.h"
+#include "ide-context.h"
+#include "ide-device.h"
+#include "ide-project.h"
+
+typedef struct
+{
+  GKeyFile  *config;
+  IdeDevice *device;
+  GFile     *directory;
+  guint      executed : 1;
+} IdeAutotoolsBuildTaskPrivate;
+
+typedef struct
+{
+  gchar  *directory_path;
+  gchar  *project_path;
+  gchar  *system_type;
+  gchar **configure_argv;
+} WorkerState;
+
+typedef gboolean (*WorkStep) (GTask                 *task,
+                              IdeAutotoolsBuildTask *self,
+                              WorkerState           *state,
+                              GCancellable          *cancellable);
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeAutotoolsBuildTask, ide_autotools_build_task,
+                            IDE_TYPE_BUILD_RESULT)
+
+enum {
+  PROP_0,
+  PROP_CONFIG,
+  PROP_DEVICE,
+  PROP_DIRECTORY,
+  LAST_PROP
+};
+
+static GSubprocess *log_and_spawn  (IdeAutotoolsBuildTask  *self,
+                                    GSubprocessLauncher    *launcher,
+                                    GError                **error,
+                                    const gchar            *argv0,
+                                    ...) G_GNUC_NULL_TERMINATED;
+static gboolean step_mkdirs        (GTask                  *task,
+                                    IdeAutotoolsBuildTask  *self,
+                                    WorkerState            *state,
+                                    GCancellable           *cancellable);
+static gboolean step_autogen       (GTask                  *task,
+                                    IdeAutotoolsBuildTask  *self,
+                                    WorkerState            *state,
+                                    GCancellable           *cancellable);
+static gboolean step_configure     (GTask                  *task,
+                                    IdeAutotoolsBuildTask  *self,
+                                    WorkerState            *state,
+                                    GCancellable           *cancellable);
+static gboolean step_make_all      (GTask                  *task,
+                                    IdeAutotoolsBuildTask  *self,
+                                    WorkerState            *state,
+                                    GCancellable           *cancellable);
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+static WorkStep gWorkSteps [] = {
+  step_mkdirs,
+  step_autogen,
+  step_configure,
+  step_make_all,
+  NULL
+};
+
+/**
+ * ide_autotools_build_task_get_config:
+ * @self: A #IdeAutotoolsBuildTask.
+ *
+ * Gets the "config" property of the task. This is the overlay config to be
+ * applied on top of the device config when compiling.
+ *
+ * Returns: (transfer none) (nullable): A #GKeyFile or %NULL.
+ */
+GKeyFile *
+ide_autotools_build_task_get_config (IdeAutotoolsBuildTask *self)
+{
+  IdeAutotoolsBuildTaskPrivate *priv;
+
+  g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILD_TASK (self), NULL);
+
+  priv = ide_autotools_build_task_get_instance_private (self);
+
+  return priv->config;
+}
+
+static void
+ide_autotools_build_task_set_config (IdeAutotoolsBuildTask *self,
+                                     GKeyFile              *config)
+{
+  IdeAutotoolsBuildTaskPrivate *priv;
+
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_TASK (self));
+
+  priv = ide_autotools_build_task_get_instance_private (self);
+
+  if (priv->config != config)
+    {
+      g_clear_pointer (&priv->config, g_key_file_unref);
+      priv->config = config ? g_key_file_ref (config) : NULL;
+      g_object_notify_by_pspec (G_OBJECT (self),
+                                gParamSpecs [PROP_CONFIG]);
+    }
+}
+
+/**
+ * ide_autotools_build_task_get_device:
+ * @self: A #IdeAutotoolsBuildTask.
+ *
+ * Gets the "device" property. This is the device we are compiling for,
+ * which may involve cross-compiling.
+ *
+ * Returns: (transfer none): An #IdeDevice.
+ */
+IdeDevice *
+ide_autotools_build_task_get_device (IdeAutotoolsBuildTask *self)
+{
+  IdeAutotoolsBuildTaskPrivate *priv;
+
+  g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILD_TASK (self), NULL);
+
+  priv = ide_autotools_build_task_get_instance_private (self);
+
+  return priv->device;
+}
+
+static void
+ide_autotools_build_task_set_device (IdeAutotoolsBuildTask *self,
+                                     IdeDevice             *device)
+{
+  IdeAutotoolsBuildTaskPrivate *priv;
+
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_TASK (self));
+
+  priv = ide_autotools_build_task_get_instance_private (self);
+
+  if (g_set_object (&priv->device, device))
+    g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_DEVICE]);
+}
+
+/**
+ * ide_autotools_build_task_get_directory:
+ *
+ * Fetches the build directory that was used.
+ *
+ * Returns: (transfer none): A #GFile.
+ */
+GFile *
+ide_autotools_build_task_get_directory (IdeAutotoolsBuildTask *self)
+{
+  IdeAutotoolsBuildTaskPrivate *priv;
+
+  g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILD_TASK (self), NULL);
+
+  priv = ide_autotools_build_task_get_instance_private (self);
+
+  return priv->directory;
+}
+
+static void
+ide_autotools_build_task_set_directory (IdeAutotoolsBuildTask *self,
+                                        GFile                 *directory)
+{
+  IdeAutotoolsBuildTaskPrivate *priv;
+
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_TASK (self));
+  g_return_if_fail (!directory || G_IS_FILE (directory));
+
+  priv = ide_autotools_build_task_get_instance_private (self);
+
+  /*
+   * We require a build directory that is accessable via a native path.
+   */
+  if (directory)
+    {
+      g_autoptr(gchar) path;
+
+      path = g_file_get_path (directory);
+
+      if (!path)
+        {
+          g_warning (_("Directory must be on a locally mounted filesystem."));
+          return;
+        }
+    }
+
+  if (priv->directory != directory)
+    if (g_set_object (&priv->directory, directory))
+      g_object_notify_by_pspec (G_OBJECT (self),
+                                gParamSpecs [PROP_DIRECTORY]);
+}
+
+static void
+ide_autotools_build_task_finalize (GObject *object)
+{
+  IdeAutotoolsBuildTask *self = (IdeAutotoolsBuildTask *)object;
+  IdeAutotoolsBuildTaskPrivate *priv;
+
+  priv = ide_autotools_build_task_get_instance_private (self);
+
+  g_clear_object (&priv->device);
+  g_clear_object (&priv->directory);
+  g_clear_pointer (&priv->config, g_key_file_unref);
+
+  G_OBJECT_CLASS (ide_autotools_build_task_parent_class)->finalize (object);
+}
+
+static void
+ide_autotools_build_task_get_property (GObject    *object,
+                                       guint       prop_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+  IdeAutotoolsBuildTask *self = IDE_AUTOTOOLS_BUILD_TASK (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONFIG:
+      g_value_set_object (value,
+                          ide_autotools_build_task_get_config (self));
+      break;
+
+    case PROP_DEVICE:
+      g_value_set_object (value,
+                          ide_autotools_build_task_get_device (self));
+      break;
+
+    case PROP_DIRECTORY:
+      g_value_set_object (value,
+                          ide_autotools_build_task_get_directory (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_autotools_build_task_set_property (GObject      *object,
+                                       guint         prop_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec)
+{
+  IdeAutotoolsBuildTask *self = IDE_AUTOTOOLS_BUILD_TASK (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONFIG:
+      ide_autotools_build_task_set_config (self,
+                                           g_value_get_boxed (value));
+      break;
+
+    case PROP_DEVICE:
+      ide_autotools_build_task_set_device (self,
+                                           g_value_get_object (value));
+      break;
+
+    case PROP_DIRECTORY:
+      ide_autotools_build_task_set_directory (self,
+                                              g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_autotools_build_task_class_init (IdeAutotoolsBuildTaskClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_autotools_build_task_finalize;
+  object_class->get_property = ide_autotools_build_task_get_property;
+  object_class->set_property = ide_autotools_build_task_set_property;
+
+  gParamSpecs [PROP_CONFIG] =
+    g_param_spec_boxed ("config",
+                        _("Config"),
+                        _("The overlay config for the compilation."),
+                        G_TYPE_KEY_FILE,
+                        (G_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_CONFIG,
+                                   gParamSpecs [PROP_CONFIG]);
+
+  gParamSpecs [PROP_DEVICE] =
+    g_param_spec_object ("device",
+                         _("Device"),
+                         _("The device to build for."),
+                         IDE_TYPE_DEVICE,
+                         (G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_DEVICE,
+                                   gParamSpecs [PROP_DEVICE]);
+
+  gParamSpecs [PROP_DIRECTORY] =
+    g_param_spec_object ("directory",
+                         _("Directory"),
+                         _("The directory to perform the build within."),
+                         G_TYPE_FILE,
+                         (G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_DIRECTORY,
+                                   gParamSpecs [PROP_DIRECTORY]);
+}
+
+static void
+ide_autotools_build_task_init (IdeAutotoolsBuildTask *self)
+{
+}
+
+static gchar **
+gen_configure_argv (IdeAutotoolsBuildTask *self,
+                    WorkerState           *state)
+{
+  IdeAutotoolsBuildTaskPrivate *priv;
+  GKeyFile *configs[2];
+  GPtrArray *ar;
+  GHashTable *ht;
+  gpointer k, v;
+  GHashTableIter iter;
+  gchar *configure_path;
+  guint j;
+
+  g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILD_TASK (self), NULL);
+
+  priv = ide_autotools_build_task_get_instance_private (self);
+
+  ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+  configs [0] = ide_device_get_config (priv->device);
+  configs [1] = priv->config;
+
+  for (j = 0; j < G_N_ELEMENTS (configs); j++)
+    {
+      GKeyFile *config = configs [j];
+
+      if (config)
+        {
+          if (g_key_file_has_group (config, "autoconf"))
+            {
+              gchar **keys;
+              gsize len;
+              gsize i;
+
+              keys = g_key_file_get_keys (config, "autoconf", &len, NULL);
+
+              for (i = 0; i < len; i++)
+                {
+                  gchar *value;
+
+                  if (*keys [i] == '-')
+                    {
+                      value = g_key_file_get_string (config,
+                                                     "autoconf", keys [i],
+                                                     NULL);
+                      if (value)
+                        g_hash_table_replace (ht, g_strdup (keys [i]), value);
+                    }
+                }
+
+              g_strfreev (keys);
+            }
+        }
+    }
+
+  ar = g_ptr_array_new ();
+  configure_path = g_build_filename (state->project_path, "configure", NULL);
+  g_ptr_array_add (ar, configure_path);
+
+  g_hash_table_iter_init (&iter, ht);
+
+  while (g_hash_table_iter_next (&iter, &k, &v))
+    {
+      g_ptr_array_add (ar, g_strdup (k));
+      if (v && *(gchar *)v)
+        g_ptr_array_add (ar, g_strdup (v));
+    }
+
+  if (!g_hash_table_lookup (ht, "--prefix"))
+    {
+      gchar *prefix;
+
+      prefix = g_build_filename (state->project_path, "_install", NULL);
+      g_ptr_array_add (ar, g_strdup_printf ("--prefix=%s", prefix));
+      g_free (prefix);
+    }
+
+  g_ptr_array_add (ar, NULL);
+  g_hash_table_unref (ht);
+
+  return (gchar **)g_ptr_array_free (ar, FALSE);
+}
+
+static WorkerState *
+worker_state_new (IdeAutotoolsBuildTask *self)
+{
+  IdeAutotoolsBuildTaskPrivate *priv;
+  g_autoptr(gchar) name = NULL;
+  IdeContext *context;
+  GFile *project_dir;
+  GFile *project_file;
+  WorkerState *state;
+
+  g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILD_TASK (self), NULL);
+
+  priv = ide_autotools_build_task_get_instance_private (self);
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  project_file = ide_context_get_project_file (context);
+
+  name = g_file_get_basename (project_file);
+
+  if (g_str_has_prefix (name, "configure."))
+    project_dir = g_file_get_parent (project_file);
+  else
+    project_dir = g_object_ref (project_file);
+
+  state = g_slice_new0 (WorkerState);
+
+  state->directory_path = g_file_get_path (priv->directory);
+  state->project_path = g_file_get_path (project_dir);
+  state->system_type = g_strdup (ide_device_get_system_type (priv->device));
+  state->configure_argv = gen_configure_argv (self, state);
+
+  return state;
+}
+
+static void
+worker_state_free (void *data)
+{
+  WorkerState *state = data;
+
+  g_free (state->directory_path);
+  g_free (state->project_path);
+  g_free (state->system_type);
+  g_slice_free (WorkerState, state);
+}
+
+static void
+ide_autotools_build_task_execute_worker (GTask        *task,
+                                         gpointer      source_object,
+                                         gpointer      task_data,
+                                         GCancellable *cancellable)
+{
+  IdeAutotoolsBuildTask *self = source_object;
+  WorkerState *state = task_data;
+  guint i;
+
+  g_return_if_fail (G_IS_TASK (task));
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_TASK (self));
+  g_return_if_fail (state);
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  for (i = 0; gWorkSteps [i]; i++)
+    {
+      if (g_cancellable_is_cancelled (cancellable) ||
+          !gWorkSteps [i] (task, self, state, cancellable))
+        return;
+    }
+
+  g_task_return_boolean (task, TRUE);
+}
+
+void
+ide_autotools_build_task_execute_async (IdeAutotoolsBuildTask *self,
+                                        GCancellable          *cancellable,
+                                        GAsyncReadyCallback    callback,
+                                        gpointer               user_data)
+{
+  IdeAutotoolsBuildTaskPrivate *priv;
+  g_autoptr(GTask) task = NULL;
+  WorkerState *state;
+
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_TASK (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  priv = ide_autotools_build_task_get_instance_private (self);
+
+  if (priv->executed)
+    {
+      g_task_report_new_error (self, callback, user_data,
+                               ide_autotools_build_task_execute_async,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               _("Cannot execute build task more than once."));
+      return;
+    }
+
+  priv->executed = TRUE;
+
+  state = worker_state_new (self);
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_task_data (task, state, worker_state_free);
+  g_task_run_in_thread (task, ide_autotools_build_task_execute_worker);
+}
+
+gboolean
+ide_autotools_build_task_execute_finish (IdeAutotoolsBuildTask  *self,
+                                         GAsyncResult           *result,
+                                         GError                **error)
+{
+  GTask *task = (GTask *)result;
+
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_TASK (self));
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+  g_return_val_if_fail (G_IS_TASK (task), FALSE);
+
+  return g_task_propagate_boolean (task, error);
+}
+
+static GSubprocess *
+log_and_spawn (IdeAutotoolsBuildTask  *self,
+               GSubprocessLauncher    *launcher,
+               GError                **error,
+               const gchar           *argv0,
+               ...)
+{
+  GSubprocess *ret;
+  GPtrArray *argv;
+  GString *log;
+  gchar *item;
+  va_list args;
+
+  log = g_string_new (NULL);
+  g_string_append (log, argv0);
+
+  argv = g_ptr_array_new ();
+  g_ptr_array_add (argv, (gchar *)argv0);
+
+  va_start (args, argv0);
+  while ((item = va_arg (args, gchar *)))
+    {
+      g_ptr_array_add (argv, item);
+      g_string_append_printf (log, " '%s'", item);
+    }
+  va_end (args);
+
+  g_ptr_array_add (argv, NULL);
+
+  ide_build_result_log_stdout (IDE_BUILD_RESULT (self), log->str);
+  ret = g_subprocess_launcher_spawnv (launcher,
+                                      (const gchar * const *)argv->pdata,
+                                      error);
+
+  g_string_free (log, TRUE);
+  g_ptr_array_unref (argv);
+
+  return ret;
+}
+
+static gboolean
+step_mkdirs (GTask                 *task,
+             IdeAutotoolsBuildTask *self,
+             WorkerState           *state,
+             GCancellable          *cancellable)
+{
+  g_assert (G_IS_TASK (task));
+  g_assert (IDE_IS_AUTOTOOLS_BUILD_TASK (self));
+  g_assert (state);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  if (!g_file_test (state->directory_path, G_FILE_TEST_EXISTS))
+    {
+      if (g_mkdir_with_parents (state->directory_path, 0750) != 0)
+        {
+          g_task_return_new_error (task,
+                                   G_IO_ERROR,
+                                   G_IO_ERROR_FAILED,
+                                   _("Failed to create build directory."));
+          return FALSE;
+        }
+    }
+  else if (!g_file_test (state->directory_path, G_FILE_TEST_IS_DIR))
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_NOT_DIRECTORY,
+                               _("'%s' is not a directory."),
+                               state->directory_path);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+step_autogen (GTask                 *task,
+              IdeAutotoolsBuildTask *self,
+              WorkerState           *state,
+              GCancellable          *cancellable)
+{
+  g_autoptr(gchar) autogen_sh_path = NULL;
+  g_autoptr(gchar) configure_path = NULL;
+  g_autoptr(GSubprocessLauncher) launcher = NULL;
+  g_autoptr(GSubprocess) process = NULL;
+  GError *error = NULL;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (IDE_IS_AUTOTOOLS_BUILD_TASK (self));
+  g_assert (state);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  configure_path = g_build_filename (state->project_path,
+                                     "configure",
+                                     NULL);
+
+  if (g_file_test (configure_path, G_FILE_TEST_IS_REGULAR))
+    return TRUE;
+
+  autogen_sh_path = g_build_filename (state->project_path,
+                                      "autogen.sh",
+                                      NULL);
+
+  if (!g_file_test (autogen_sh_path, G_FILE_TEST_EXISTS))
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               _("autogen.sh is missing from project directory."));
+      return FALSE;
+    }
+
+  if (!g_file_test (autogen_sh_path, G_FILE_TEST_IS_EXECUTABLE))
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               _("autogen.sh is not executable."));
+      return FALSE;
+    }
+
+  launcher = g_subprocess_launcher_new ((G_SUBPROCESS_FLAGS_STDOUT_PIPE |
+                                         G_SUBPROCESS_FLAGS_STDERR_PIPE));
+  g_subprocess_launcher_set_cwd (launcher, state->project_path);
+  g_subprocess_launcher_setenv (launcher, "NOCONFIGURE", "1", TRUE);
+
+  process = log_and_spawn (self, launcher, &error, autogen_sh_path, NULL);
+
+  if (!process)
+    {
+      g_task_return_error (task, error);
+      return FALSE;
+    }
+
+  ide_build_result_log_subprocess (IDE_BUILD_RESULT (self), process);
+
+  if (!g_subprocess_wait_check (process, cancellable, &error))
+    {
+      g_task_return_error (task, error);
+      return FALSE;
+    }
+
+  if (!g_file_test (configure_path, G_FILE_TEST_IS_REGULAR))
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               _("autogen.sh failed to create configure."));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+step_configure (GTask                 *task,
+                IdeAutotoolsBuildTask *self,
+                WorkerState           *state,
+                GCancellable          *cancellable)
+{
+  g_autoptr(GSubprocessLauncher) launcher = NULL;
+  g_autoptr(GSubprocess) process = NULL;
+  g_autoptr(gchar) makefile_path = NULL;
+  g_autoptr(gchar) config_log = NULL;
+  GError *error = NULL;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (IDE_IS_AUTOTOOLS_BUILD_TASK (self));
+  g_assert (state);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  /*
+   * If we have a Makefile already, we can skip the call to configure.
+   *
+   * TODO: Plumb support for "Full Rebuilds", which include autogen, configure,
+   *       and make.
+   */
+
+  makefile_path = g_build_filename (state->directory_path, "Makefile", NULL);
+  if (g_file_test (makefile_path, G_FILE_TEST_EXISTS))
+    return TRUE;
+
+  launcher = g_subprocess_launcher_new ((G_SUBPROCESS_FLAGS_STDERR_PIPE |
+                                         G_SUBPROCESS_FLAGS_STDOUT_PIPE));
+  g_subprocess_launcher_set_cwd (launcher, state->directory_path);
+
+  config_log = g_strjoinv (" ", state->configure_argv);
+  ide_build_result_log_stdout (IDE_BUILD_RESULT (self), config_log);
+
+  process = g_subprocess_launcher_spawnv (
+      launcher,
+      (const gchar * const *)state->configure_argv,
+      &error);
+
+  if (!process)
+    {
+      g_task_return_error (task, error);
+      return FALSE;
+    }
+
+  ide_build_result_log_subprocess (IDE_BUILD_RESULT (self), process);
+
+  if (!g_subprocess_wait_check (process, cancellable, &error))
+    {
+      g_task_return_error (task, error);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+step_make_all  (GTask                 *task,
+                IdeAutotoolsBuildTask *self,
+                WorkerState           *state,
+                GCancellable          *cancellable)
+{
+  g_autoptr(GSubprocessLauncher) launcher = NULL;
+  g_autoptr(GSubprocess) process = NULL;
+  GError *error = NULL;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (IDE_IS_AUTOTOOLS_BUILD_TASK (self));
+  g_assert (state);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  launcher = g_subprocess_launcher_new ((G_SUBPROCESS_FLAGS_STDERR_PIPE |
+                                         G_SUBPROCESS_FLAGS_STDOUT_PIPE));
+  g_subprocess_launcher_set_cwd (launcher, state->directory_path);
+
+  process = log_and_spawn (self, launcher, &error, "make", "all", NULL);
+
+  if (!process)
+    {
+      g_task_return_error (task, error);
+      return FALSE;
+    }
+
+  ide_build_result_log_subprocess (IDE_BUILD_RESULT (self), process);
+
+  if (!g_subprocess_wait_check (process, cancellable, &error))
+    {
+      g_task_return_error (task, error);
+      return FALSE;
+    }
+
+  return TRUE;
+}
diff --git a/libide/autotools/ide-autotools-build-task.h b/libide/autotools/ide-autotools-build-task.h
new file mode 100644
index 0000000..552e3ad
--- /dev/null
+++ b/libide/autotools/ide-autotools-build-task.h
@@ -0,0 +1,49 @@
+/* ide-autotools-build-result.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_AUTOTOOLS_BUILD_TASK_H
+#define IDE_AUTOTOOLS_BUILD_TASK_H
+
+#include <gio/gio.h>
+
+#include "ide-build-result.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_AUTOTOOLS_BUILD_TASK (ide_autotools_build_task_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeAutotoolsBuildTask, ide_autotools_build_task,
+                      IDE, AUTOTOOLS_BUILD_TASK, IdeBuildResult)
+
+struct _IdeAutotoolsBuildTask
+{
+  IdeBuildResult parent_instance;
+};
+
+GFile    *ide_autotools_build_task_get_directory  (IdeAutotoolsBuildTask  *self);
+void      ide_autotools_build_task_execute_async  (IdeAutotoolsBuildTask  *self,
+                                                   GCancellable           *cancellable,
+                                                   GAsyncReadyCallback     callback,
+                                                   gpointer                user_data);
+gboolean  ide_autotools_build_task_execute_finish (IdeAutotoolsBuildTask  *self,
+                                                   GAsyncResult           *result,
+                                                   GError                **error);
+
+G_END_DECLS
+
+#endif /* IDE_AUTOTOOLS_BUILD_TASK_H */
diff --git a/libide/autotools/ide-autotools-builder.c b/libide/autotools/ide-autotools-builder.c
new file mode 100644
index 0000000..4b37918
--- /dev/null
+++ b/libide/autotools/ide-autotools-builder.c
@@ -0,0 +1,298 @@
+/* ide-autotools-builder.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-autotools-build-task.h"
+#include "ide-autotools-builder.h"
+#include "ide-build-result.h"
+#include "ide-context.h"
+#include "ide-device.h"
+#include "ide-project.h"
+
+typedef struct
+{
+  GKeyFile  *config;
+  IdeDevice *device;
+} IdeAutotoolsBuilderPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeAutotoolsBuilder, ide_autotools_builder,
+                            IDE_TYPE_BUILDER)
+
+enum {
+  PROP_0,
+  PROP_CONFIG,
+  PROP_DEVICE,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+GKeyFile *
+ide_autotools_builder_get_config (IdeAutotoolsBuilder *builder)
+{
+  IdeAutotoolsBuilderPrivate *priv;
+
+  g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILDER (builder), NULL);
+
+  priv = ide_autotools_builder_get_instance_private (builder);
+
+  return priv->config;
+}
+
+static void
+ide_autotools_builder_set_config (IdeAutotoolsBuilder *builder,
+                                  GKeyFile            *config)
+{
+  IdeAutotoolsBuilderPrivate *priv;
+
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILDER (builder));
+
+  priv = ide_autotools_builder_get_instance_private (builder);
+
+  if (priv->config != config)
+    {
+      g_clear_pointer (&priv->config, g_key_file_unref);
+      if (config)
+        priv->config = g_key_file_ref (config);
+      g_object_notify_by_pspec (G_OBJECT (builder),
+                                gParamSpecs [PROP_CONFIG]);
+    }
+}
+
+IdeDevice *
+ide_autotools_builder_get_device (IdeAutotoolsBuilder *builder)
+{
+  IdeAutotoolsBuilderPrivate *priv;
+
+  g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILDER (builder), NULL);
+
+  priv = ide_autotools_builder_get_instance_private (builder);
+
+  return priv->device;
+}
+
+static void
+ide_autotools_builder_set_device (IdeAutotoolsBuilder *builder,
+                                  IdeDevice           *device)
+{
+  IdeAutotoolsBuilderPrivate *priv;
+
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILDER (builder));
+  g_return_if_fail (!device || IDE_IS_DEVICE (device));
+
+  priv = ide_autotools_builder_get_instance_private (builder);
+
+  if (priv->device != device)
+    if (g_set_object (&priv->device, device))
+      g_object_notify_by_pspec (G_OBJECT (builder), gParamSpecs [PROP_DEVICE]);
+}
+
+static void
+ide_autotools_builder_build_cb (GObject      *object,
+                                GAsyncResult *result,
+                                gpointer      user_data)
+{
+  g_autoptr(GTask) task = user_data;
+  IdeAutotoolsBuildTask *build_result = (IdeAutotoolsBuildTask *)object;
+  GError *error = NULL;
+
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_TASK (build_result));
+  g_return_if_fail (G_IS_TASK (task));
+
+  if (!ide_autotools_build_task_execute_finish (build_result, result, &error))
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  g_task_return_pointer (task, g_object_ref (build_result), g_object_unref);
+}
+
+static void
+ide_autotools_builder_build_async (IdeBuilder           *builder,
+                                   IdeBuildResult      **result,
+                                   GCancellable         *cancellable,
+                                   GAsyncReadyCallback   callback,
+                                   gpointer              user_data)
+{
+  IdeAutotoolsBuilderPrivate *priv;
+  IdeAutotoolsBuilder *self = (IdeAutotoolsBuilder *)builder;
+  g_autoptr(IdeAutotoolsBuildTask) build_result = NULL;
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(gchar) build_dir = NULL;
+  g_autoptr(GFile) directory = NULL;
+  const gchar *device_id;
+  const gchar *project_name;
+  const gchar *root_build_dir;
+  const gchar *system_type;
+  IdeContext *context;
+  IdeDevice *device;
+  IdeProject *project;
+
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILDER (builder));
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILDER (self));
+
+  priv = ide_autotools_builder_get_instance_private (self);
+
+  task = g_task_new (self, cancellable, callback, user_data);
+
+  device = ide_autotools_builder_get_device (self);
+  device_id = ide_device_get_id (device);
+  system_type = ide_device_get_system_type (device);
+
+  context = ide_object_get_context (IDE_OBJECT (builder));
+  root_build_dir = ide_context_get_root_build_dir (context);
+
+  project = ide_context_get_project (context);
+  project_name = ide_project_get_name (project);
+
+  build_dir = g_build_filename (root_build_dir,
+                                project_name,
+                                device_id,
+                                system_type,
+                                NULL);
+  directory = g_file_new_for_path (build_dir);
+
+  build_result = g_object_new (IDE_TYPE_AUTOTOOLS_BUILD_TASK,
+                               "context", context,
+                               "config", priv->config,
+                               "device", device,
+                               "directory", directory,
+                               NULL);
+
+  if (result)
+    *result = g_object_ref (build_result);
+
+  ide_autotools_build_task_execute_async (build_result,
+                                          cancellable,
+                                          ide_autotools_builder_build_cb,
+                                          g_object_ref (task));
+}
+
+static IdeBuildResult *
+ide_autotools_builder_build_finish (IdeBuilder    *builder,
+                                    GAsyncResult  *result,
+                                    GError       **error)
+{
+  GTask *task = (GTask *)result;
+
+  g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILDER (builder), NULL);
+  g_return_val_if_fail (G_IS_TASK (task), NULL);
+
+  return g_task_propagate_pointer (task, error);
+}
+
+static void
+ide_autotools_builder_finalize (GObject *object)
+{
+  IdeAutotoolsBuilder *self = (IdeAutotoolsBuilder *)object;
+  IdeAutotoolsBuilderPrivate *priv = ide_autotools_builder_get_instance_private (self);
+
+  g_clear_object (&priv->config);
+  g_clear_object (&priv->device);
+
+  G_OBJECT_CLASS (ide_autotools_builder_parent_class)->finalize (object);
+}
+
+static void
+ide_autotools_builder_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  IdeAutotoolsBuilder *self = IDE_AUTOTOOLS_BUILDER (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONFIG:
+      g_value_set_boxed (value, ide_autotools_builder_get_config (self));
+      break;
+
+    case PROP_DEVICE:
+      g_value_set_object (value, ide_autotools_builder_get_device (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_autotools_builder_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  IdeAutotoolsBuilder *self = IDE_AUTOTOOLS_BUILDER (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONFIG:
+      ide_autotools_builder_set_config (self, g_value_get_boxed (value));
+      break;
+
+    case PROP_DEVICE:
+      ide_autotools_builder_set_device (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_autotools_builder_class_init (IdeAutotoolsBuilderClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeBuilderClass *builder_class = IDE_BUILDER_CLASS (klass);
+
+  object_class->finalize = ide_autotools_builder_finalize;
+  object_class->get_property = ide_autotools_builder_get_property;
+  object_class->set_property = ide_autotools_builder_set_property;
+
+  builder_class->build_async = ide_autotools_builder_build_async;
+  builder_class->build_finish = ide_autotools_builder_build_finish;
+
+  gParamSpecs [PROP_CONFIG] =
+    g_param_spec_boxed ("config",
+                        _("Config"),
+                        _("The configuration for the build."),
+                        G_TYPE_KEY_FILE,
+                        (G_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_CONFIG,
+                                   gParamSpecs [PROP_CONFIG]);
+
+  gParamSpecs [PROP_DEVICE] =
+    g_param_spec_object ("device",
+                         _("Device"),
+                         _("The device to build for."),
+                         IDE_TYPE_DEVICE,
+                         (G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_DEVICE,
+                                   gParamSpecs [PROP_DEVICE]);
+}
+
+static void
+ide_autotools_builder_init (IdeAutotoolsBuilder *self)
+{
+}
diff --git a/libide/autotools/ide-autotools-builder.h b/libide/autotools/ide-autotools-builder.h
new file mode 100644
index 0000000..fe14552
--- /dev/null
+++ b/libide/autotools/ide-autotools-builder.h
@@ -0,0 +1,38 @@
+/* ide-autotools-builder.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_AUTOTOOLS_BUILDER_H
+#define IDE_AUTOTOOLS_BUILDER_H
+
+#include "ide-builder.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_AUTOTOOLS_BUILDER (ide_autotools_builder_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeAutotoolsBuilder, ide_autotools_builder,
+                          IDE, AUTOTOOLS_BUILDER, IdeBuilder)
+
+struct _IdeAutotoolsBuilderClass
+{
+  IdeBuilderClass parent;
+};
+
+G_END_DECLS
+
+#endif /* IDE_AUTOTOOLS_BUILDER_H */
diff --git a/libide/directory/ide-directory-build-system.c b/libide/directory/ide-directory-build-system.c
new file mode 100644
index 0000000..b0431b6
--- /dev/null
+++ b/libide/directory/ide-directory-build-system.c
@@ -0,0 +1,141 @@
+/* ide-directory-build-system.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-context.h"
+#include "ide-directory-build-system.h"
+#include "ide-project.h"
+#include "ide-project-file.h"
+#include "ide-project-item.h"
+
+typedef struct
+{
+  gpointer dummy;
+} IdeDirectoryBuildSystemPrivate;
+
+static void async_initiable_init (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (IdeDirectoryBuildSystem,
+                        ide_directory_build_system,
+                        IDE_TYPE_BUILD_SYSTEM,
+                        0,
+                        G_ADD_PRIVATE (IdeDirectoryBuildSystem)
+                        G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
+                                               async_initiable_init))
+
+enum {
+  PROP_0,
+  LAST_PROP
+};
+
+//static GParamSpec *gParamSpecs [LAST_PROP];
+
+IdeDirectoryBuildSystem *
+ide_directory_build_system_new (void)
+{
+  return g_object_new (IDE_TYPE_DIRECTORY_BUILD_SYSTEM, NULL);
+}
+
+static void
+ide_directory_build_system_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (ide_directory_build_system_parent_class)->finalize (object);
+}
+
+static void
+ide_directory_build_system_get_property (GObject    *object,
+                                         guint       prop_id,
+                                         GValue     *value,
+                                         GParamSpec *pspec)
+{
+  //IdeDirectoryBuildSystem *self = IDE_DIRECTORY_BUILD_SYSTEM (object);
+
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_directory_build_system_set_property (GObject      *object,
+                                         guint         prop_id,
+                                         const GValue *value,
+                                         GParamSpec   *pspec)
+{
+  //IdeDirectoryBuildSystem *self = IDE_DIRECTORY_BUILD_SYSTEM (object);
+
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_directory_build_system_class_init (IdeDirectoryBuildSystemClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_directory_build_system_finalize;
+  object_class->get_property = ide_directory_build_system_get_property;
+  object_class->set_property = ide_directory_build_system_set_property;
+}
+
+static void
+ide_directory_build_system_init (IdeDirectoryBuildSystem *self)
+{
+}
+
+static void
+ide_directory_build_system_init_async (GAsyncInitable      *initable,
+                                       int                  io_priority,
+                                       GCancellable        *cancellable,
+                                       GAsyncReadyCallback  callback,
+                                       gpointer             user_data)
+{
+  IdeDirectoryBuildSystem *system = (IdeDirectoryBuildSystem *)initable;
+  GTask *task;
+
+  g_return_if_fail (IDE_IS_DIRECTORY_BUILD_SYSTEM (system));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (system, cancellable, callback, user_data);
+  g_task_return_boolean (task, TRUE);
+  g_object_unref (task);
+}
+
+static gboolean
+ide_directory_build_system_init_finish (GAsyncInitable  *initable,
+                                        GAsyncResult    *result,
+                                        GError         **error)
+{
+  GTask *task = (GTask *)result;
+
+  g_return_val_if_fail (G_IS_TASK (task), FALSE);
+
+  return g_task_propagate_boolean (task, error);
+}
+
+static void
+async_initiable_init (GAsyncInitableIface *iface)
+{
+  iface->init_async = ide_directory_build_system_init_async;
+  iface->init_finish = ide_directory_build_system_init_finish;
+}
diff --git a/libide/directory/ide-directory-build-system.h b/libide/directory/ide-directory-build-system.h
new file mode 100644
index 0000000..5abfe00
--- /dev/null
+++ b/libide/directory/ide-directory-build-system.h
@@ -0,0 +1,38 @@
+/* ide-directory-build-system.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_DIRECTORY_BUILD_SYSTEM_H
+#define IDE_DIRECTORY_BUILD_SYSTEM_H
+
+#include "ide-build-system.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DIRECTORY_BUILD_SYSTEM (ide_directory_build_system_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeDirectoryBuildSystem, ide_directory_build_system,
+                      IDE, DIRECTORY_BUILD_SYSTEM, IdeBuildSystem)
+
+struct _IdeDirectoryBuildSystem
+{
+  IdeBuildSystem parent_instance;
+};
+
+G_END_DECLS
+
+#endif /* IDE_DIRECTORY_BUILD_SYSTEM_H */
diff --git a/libide/directory/ide-directory-vcs.c b/libide/directory/ide-directory-vcs.c
new file mode 100644
index 0000000..b79f8ee
--- /dev/null
+++ b/libide/directory/ide-directory-vcs.c
@@ -0,0 +1,168 @@
+/* ide-directory-vcs.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-context.h"
+#include "ide-directory-vcs.h"
+#include "ide-project.h"
+#include "ide-project-files.h"
+#include "tasks/ide-load-directory-task.h"
+
+typedef struct
+{
+  gpointer dummy;
+} IdeDirectoryVcsPrivate;
+
+enum
+{
+  PROP_0,
+  LAST_PROP
+};
+
+static void async_initable_iface_init (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (IdeDirectoryVcs, ide_directory_vcs, IDE_TYPE_VCS, 0,
+                        G_ADD_PRIVATE (IdeDirectoryVcs)
+                        G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
+                                               async_initable_iface_init))
+
+#if 0
+static GParamSpec *gParamSpecs [LAST_PROP];
+#endif
+
+static void
+ide_directory_vcs_finalize (GObject *object)
+{
+#if 0
+  IdeDirectoryVcs *self = (IdeDirectoryVcs *)object;
+  IdeDirectoryVcsPrivate *priv = ide_directory_vcs_get_instance_private (self);
+#endif
+
+  G_OBJECT_CLASS (ide_directory_vcs_parent_class)->finalize (object);
+}
+
+static void
+ide_directory_vcs_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+#if 0
+  IdeDirectoryVcs *vcs = IDE_DIRECTORY_VCS (object);
+#endif
+
+  switch (prop_id) {
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ide_directory_vcs_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+#if 0
+  IdeDirectoryVcs *vcs = IDE_DIRECTORY_VCS (object);
+#endif
+
+  switch (prop_id) {
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ide_directory_vcs_class_init (IdeDirectoryVcsClass *klass)
+{
+  GObjectClass *object_class;
+
+  object_class = G_OBJECT_CLASS (klass);
+  object_class->finalize = ide_directory_vcs_finalize;
+  object_class->get_property = ide_directory_vcs_get_property;
+  object_class->set_property = ide_directory_vcs_set_property;
+}
+
+static void
+ide_directory_vcs_init (IdeDirectoryVcs *self)
+{
+}
+
+static void
+ide_directory_vcs_init_async (GAsyncInitable      *initable,
+                              int                  io_priority,
+                              GCancellable        *cancellable,
+                              GAsyncReadyCallback  callback,
+                              gpointer             user_data)
+{
+  IdeDirectoryVcs *self = (IdeDirectoryVcs *)initable;
+  IdeProjectItem *root;
+  IdeProjectItem *files;
+  IdeProject *project;
+  IdeContext *context;
+  GFile *directory;
+  GTask *task;
+
+  g_return_if_fail (IDE_IS_DIRECTORY_VCS (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  context = ide_object_get_context (IDE_OBJECT (initable));
+  directory = ide_context_get_project_file (context);
+  project = ide_context_get_project (context);
+  root = ide_project_get_root (project);
+
+  files = g_object_new (IDE_TYPE_PROJECT_FILES,
+                        "context", context,
+                        "parent", root,
+                        NULL);
+  ide_project_item_append (root, files);
+
+  task = ide_load_directory_task_new (self,
+                                      directory,
+                                      files,
+                                      io_priority,
+                                      cancellable,
+                                      callback,
+                                      user_data);
+
+  g_object_unref (files);
+  g_object_unref (task);
+}
+
+static gboolean
+ide_directory_vcs_init_finish (GAsyncInitable  *initable,
+                               GAsyncResult    *result,
+                               GError         **error)
+{
+  GTask *task = (GTask *)result;
+
+  g_return_val_if_fail (IDE_IS_DIRECTORY_VCS (initable), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+  g_return_val_if_fail (G_IS_TASK (task), NULL);
+
+  return g_task_propagate_boolean (task, error);
+}
+
+static void
+async_initable_iface_init (GAsyncInitableIface *iface)
+{
+  iface->init_async = ide_directory_vcs_init_async;
+  iface->init_finish = ide_directory_vcs_init_finish;
+}
diff --git a/libide/directory/ide-directory-vcs.h b/libide/directory/ide-directory-vcs.h
new file mode 100644
index 0000000..447d966
--- /dev/null
+++ b/libide/directory/ide-directory-vcs.h
@@ -0,0 +1,38 @@
+/* ide-directory-vcs.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_DIRECTORY_VCS_H
+#define IDE_DIRECTORY_VCS_H
+
+#include "ide-vcs.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DIRECTORY_VCS (ide_directory_vcs_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeDirectoryVcs, ide_directory_vcs,
+                      IDE, DIRECTORY_VCS, IdeVcs)
+
+struct _IdeDirectoryVcs
+{
+  GObject parent_instance;
+};
+
+G_END_DECLS
+
+#endif /* IDE_DIRECTORY_VCS_H */
diff --git a/libide/gconstructor.h b/libide/gconstructor.h
new file mode 100644
index 0000000..df98f83
--- /dev/null
+++ b/libide/gconstructor.h
@@ -0,0 +1,94 @@
+/*
+  If G_HAS_CONSTRUCTORS is true then the compiler support *both* constructors and
+  destructors, in a sane way, including e.g. on library unload. If not you're on
+  your own.
+
+  Some compilers need #pragma to handle this, which does not work with macros,
+  so the way you need to use this is (for constructors):
+
+  #ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA
+  #pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(my_constructor)
+  #endif
+  G_DEFINE_CONSTRUCTOR(my_constructor)
+  static void my_constructor(void) {
+   ...
+  }
+
+*/
+
+#ifndef __GTK_DOC_IGNORE__
+
+#if  __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)
+
+#define G_HAS_CONSTRUCTORS 1
+
+#define G_DEFINE_CONSTRUCTOR(_func) static void __attribute__((constructor)) _func (void);
+#define G_DEFINE_DESTRUCTOR(_func) static void __attribute__((destructor)) _func (void);
+
+#elif defined (_MSC_VER) && (_MSC_VER >= 1500)
+/* Visual studio 2008 and later has _Pragma */
+
+#define G_HAS_CONSTRUCTORS 1
+
+#define G_DEFINE_CONSTRUCTOR(_func) \
+  static void _func(void); \
+  static int _func ## _wrapper(void) { _func(); return 0; } \
+  __pragma(section(".CRT$XCU",read)) \
+  __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _wrapper;
+
+#define G_DEFINE_DESTRUCTOR(_func) \
+  static void _func(void); \
+  static int _func ## _constructor(void) { atexit (_func); return 0; } \
+  __pragma(section(".CRT$XCU",read)) \
+  __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor;
+
+#elif defined (_MSC_VER)
+
+#define G_HAS_CONSTRUCTORS 1
+
+/* Pre Visual studio 2008 must use #pragma section */
+#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1
+#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1
+
+#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \
+  section(".CRT$XCU",read)
+#define G_DEFINE_CONSTRUCTOR(_func) \
+  static void _func(void); \
+  static int _func ## _wrapper(void) { _func(); return 0; } \
+  __declspec(allocate(".CRT$XCU")) static int (*p)(void) = _func ## _wrapper;
+
+#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \
+  section(".CRT$XCU",read)
+#define G_DEFINE_DESTRUCTOR(_func) \
+  static void _func(void); \
+  static int _func ## _constructor(void) { atexit (_func); return 0; } \
+  __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor;
+
+#elif defined(__SUNPRO_C)
+
+/* This is not tested, but i believe it should work, based on:
+ * http://opensource.apple.com/source/OpenSSL098/OpenSSL098-35/src/fips/fips_premain.c
+ */
+
+#define G_HAS_CONSTRUCTORS 1
+
+#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1
+#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1
+
+#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \
+  init(_func)
+#define G_DEFINE_CONSTRUCTOR(_func) \
+  static void _func(void);
+
+#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \
+  fini(_func)
+#define G_DEFINE_DESTRUCTOR(_func) \
+  static void _func(void);
+
+#else
+
+/* constructors not supported for this compiler */
+
+#endif
+
+#endif /* __GTK_DOC_IGNORE__ */
diff --git a/libide/git/ide-git-vcs.c b/libide/git/ide-git-vcs.c
new file mode 100644
index 0000000..4e65b74
--- /dev/null
+++ b/libide/git/ide-git-vcs.c
@@ -0,0 +1,316 @@
+/* ide-git-vcs.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+#include <libgit2-glib/ggit.h>
+
+#include "ide-context.h"
+#include "ide-git-vcs.h"
+#include "ide-project.h"
+#include "ide-project-file.h"
+#include "ide-project-files.h"
+
+typedef struct
+{
+  GgitRepository *repository;
+} IdeGitVcsPrivate;
+
+static void g_async_initable_init_interface (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (IdeGitVcs, ide_git_vcs, IDE_TYPE_VCS, 0,
+                        G_ADD_PRIVATE (IdeGitVcs)
+                        G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
+                                               g_async_initable_init_interface))
+
+enum {
+  PROP_0,
+  PROP_REPOSITORY,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+GgitRepository *
+ide_git_vcs_get_repository (IdeGitVcs *vcs)
+{
+  IdeGitVcsPrivate *priv = ide_git_vcs_get_instance_private (vcs);
+
+  g_return_val_if_fail (IDE_IS_GIT_VCS (vcs), NULL);
+
+  return priv->repository;
+}
+
+static void
+ide_git_vcs_finalize (GObject *object)
+{
+  IdeGitVcs *self = (IdeGitVcs *)object;
+  IdeGitVcsPrivate *priv = ide_git_vcs_get_instance_private (self);
+
+  g_clear_object (&priv->repository);
+
+  G_OBJECT_CLASS (ide_git_vcs_parent_class)->finalize (object);
+}
+
+static void
+ide_git_vcs_get_property (GObject    *object,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  IdeGitVcs *self = IDE_GIT_VCS (object);
+
+  switch (prop_id)
+    {
+    case PROP_REPOSITORY:
+      g_value_set_object (value, ide_git_vcs_get_repository (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_git_vcs_class_init (IdeGitVcsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_git_vcs_finalize;
+  object_class->get_property = ide_git_vcs_get_property;
+
+  gParamSpecs [PROP_REPOSITORY] =
+    g_param_spec_object ("repository",
+                         _("Repository"),
+                         _("The git repository for the project."),
+                         GGIT_TYPE_REPOSITORY,
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_REPOSITORY,
+                                   gParamSpecs [PROP_REPOSITORY]);
+}
+
+static void
+ide_git_vcs_init (IdeGitVcs *self)
+{
+}
+
+static void
+ide_git_vcs_reload_index_add_path (IdeGitVcs   *self,
+                                   GHashTable  *cache,
+                                   const gchar *path)
+{
+  IdeProjectItem *parent;
+  IdeProjectItem *item;
+  IdeContext *context;
+  GFileInfo *file_info = NULL;
+  gchar *dir;
+  gchar *name;
+
+  g_return_if_fail (IDE_IS_GIT_VCS (self));
+  g_return_if_fail (cache);
+  g_return_if_fail (path);
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+
+  dir = g_path_get_dirname (path);
+  name = g_path_get_basename (path);
+
+  parent = g_hash_table_lookup (cache, dir);
+
+  if (!parent)
+    {
+      ide_git_vcs_reload_index_add_path (self, cache, dir);
+      parent = g_hash_table_lookup (cache, dir);
+    }
+
+  g_assert (IDE_IS_PROJECT_ITEM (parent));
+
+  file_info = g_file_info_new ();
+  g_file_info_set_name (file_info, name);
+  g_file_info_set_display_name (file_info, name);
+
+  item = g_object_new (IDE_TYPE_PROJECT_FILE,
+                       "context", context,
+                       "parent", parent,
+                       "file-info", file_info,
+                       NULL);
+  ide_project_item_append (parent, item);
+
+  g_hash_table_insert (cache, g_strdup (path), g_object_ref (item));
+
+  g_clear_object (&file_info);
+  g_clear_object (&item);
+  g_clear_pointer (&dir, g_free);
+  g_clear_pointer (&name, g_free);
+}
+
+static gboolean
+ide_git_vcs_reload_index (IdeGitVcs  *self,
+                          GError    **error)
+{
+  IdeGitVcsPrivate *priv = ide_git_vcs_get_instance_private (self);
+  GgitIndexEntries *entries = NULL;
+  IdeProjectItem *root;
+  IdeProjectItem *files = NULL;
+  IdeContext *context;
+  IdeProject *project;
+  GgitIndex *index = NULL;
+  GHashTable *cache = NULL;
+  gboolean ret = FALSE;
+  guint count;
+  guint i;
+
+  g_return_if_fail (IDE_IS_GIT_VCS (self));
+
+  index = ggit_repository_get_index (priv->repository, error);
+  if (!index)
+    goto cleanup;
+
+  entries = ggit_index_get_entries (index);
+  if (!entries)
+    goto cleanup;
+
+  count = ggit_index_entries_size (entries);
+  cache = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                 g_free, g_object_unref);
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  project = ide_context_get_project (context);
+  root = ide_project_get_root (project);
+  files = g_object_new (IDE_TYPE_PROJECT_FILES,
+                        "context", context,
+                        "parent", root,
+                        NULL);
+  ide_project_item_append (root, files);
+
+  g_hash_table_insert (cache, g_strdup ("."), g_object_ref (files));
+
+  for (i = 0; i < count; i++)
+    {
+      GgitIndexEntry *entry;
+      const gchar *path;
+
+      entry = ggit_index_entries_get_by_index (entries, i);
+      path = ggit_index_entry_get_path (entry);
+
+      ide_git_vcs_reload_index_add_path (self, cache, path);
+
+      ggit_index_entry_unref (entry);
+    }
+
+  ret = TRUE;
+
+cleanup:
+  g_clear_pointer (&cache, g_hash_table_unref);
+  g_clear_pointer (&entries, ggit_index_entries_unref);
+  g_clear_object (&files);
+  g_clear_object (&index);
+
+  return ret;
+}
+
+static void
+ide_git_vcs_init_worker (GTask        *task,
+                         gpointer      source_object,
+                         gpointer      task_data,
+                         GCancellable *cancellable)
+{
+  IdeGitVcsPrivate *priv;
+  GgitRepository *repository = NULL;
+  IdeGitVcs *self = source_object;
+  GError *error = NULL;
+  GFile *directory = task_data;
+  GFile *location = NULL;
+
+  g_return_if_fail (G_IS_TASK (task));
+  g_return_if_fail (IDE_IS_GIT_VCS (self));
+  g_return_if_fail (G_IS_FILE (directory));
+
+  priv = ide_git_vcs_get_instance_private (self);
+
+  location = ggit_repository_discover (directory, &error);
+
+  if (!location)
+    {
+      g_task_return_error (task, error);
+      goto cleanup;
+    }
+
+  repository = ggit_repository_open (location, &error);
+
+  if (!repository)
+    {
+      g_task_return_error (task, error);
+      goto cleanup;
+    }
+
+  priv->repository = g_object_ref (repository);
+
+  if (!ide_git_vcs_reload_index (self, &error))
+    {
+      g_task_return_error (task, error);
+      goto cleanup;
+    }
+
+  g_task_return_boolean (task, TRUE);
+
+cleanup:
+  g_clear_object (&location);
+  g_clear_object (&repository);
+}
+
+static void
+ide_git_vcs_init_async (GAsyncInitable      *initable,
+                        int                  io_priority,
+                        GCancellable        *cancellable,
+                        GAsyncReadyCallback  callback,
+                        gpointer             user_data)
+{
+  IdeContext *context;
+  IdeGitVcs *self = (IdeGitVcs *)initable;
+  GFile *project_file;
+  GTask *task;
+
+  g_return_if_fail (IDE_IS_GIT_VCS (self));
+
+  context = ide_object_get_context (IDE_OBJECT (initable));
+  project_file = ide_context_get_project_file (context);
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_task_data (task, g_object_ref (project_file), g_object_unref);
+  g_task_run_in_thread (task, ide_git_vcs_init_worker);
+  g_object_unref (task);
+}
+
+static gboolean
+ide_git_vcs_init_finish (GAsyncInitable  *initable,
+                         GAsyncResult    *result,
+                         GError         **error)
+{
+  GTask *task = (GTask *)result;
+
+  g_return_val_if_fail (G_IS_TASK (task), FALSE);
+
+  return g_task_propagate_boolean (task, error);
+}
+
+static void
+g_async_initable_init_interface (GAsyncInitableIface *iface)
+{
+  iface->init_async = ide_git_vcs_init_async;
+  iface->init_finish = ide_git_vcs_init_finish;
+}
diff --git a/libide/git/ide-git-vcs.h b/libide/git/ide-git-vcs.h
new file mode 100644
index 0000000..8c507c6
--- /dev/null
+++ b/libide/git/ide-git-vcs.h
@@ -0,0 +1,41 @@
+/* ide-git-vcs.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_GIT_VCS_H
+#define IDE_GIT_VCS_H
+
+#include <libgit2-glib/ggit.h>
+
+#include "ide-vcs.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_GIT_VCS (ide_git_vcs_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeGitVcs, ide_git_vcs, IDE, GIT_VCS, IdeVcs)
+
+struct _IdeGitVcsClass
+{
+  IdeVcsClass parent;
+};
+
+GgitRepository *ide_git_vcs_get_repository (IdeGitVcs *vcs);
+
+G_END_DECLS
+
+#endif /* IDE_GIT_VCS_H */
diff --git a/libide/ide-async-helper.c b/libide/ide-async-helper.c
new file mode 100644
index 0000000..1bccefc
--- /dev/null
+++ b/libide/ide-async-helper.c
@@ -0,0 +1,85 @@
+/* ide-async-helper.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ide-async-helper.h"
+
+static void
+ide_async_helper_cb (GObject      *object,
+                     GAsyncResult *result,
+                     gpointer      user_data)
+{
+  g_autoptr(GTask) task = user_data;
+  GPtrArray *funcs;
+  GError *error = NULL;
+
+  g_return_if_fail (G_IS_TASK (task));
+  g_return_if_fail (G_IS_TASK (result));
+
+  funcs = g_task_get_task_data (task);
+
+  if (!g_task_propagate_boolean (G_TASK (result), &error))
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  g_ptr_array_remove_index (funcs, 0);
+
+  if (funcs->len)
+    {
+      IdeAsyncStep step;
+
+      step = g_ptr_array_index (funcs, 0);
+      step (g_task_get_source_object (task),
+            g_task_get_cancellable (task),
+            ide_async_helper_cb,
+            g_object_ref (task));
+    }
+  else
+    g_task_return_boolean (task, TRUE);
+}
+
+void
+ide_async_helper_run (gpointer             source_object,
+                      GCancellable        *cancellable,
+                      GAsyncReadyCallback  callback,
+                      gpointer             user_data,
+                      IdeAsyncStep         step1,
+                      ...)
+{
+  g_autoptr(GTask) task = NULL;
+  IdeAsyncStep step;
+  GPtrArray *funcs;
+  va_list args;
+
+  g_return_if_fail (step1);
+
+  funcs = g_ptr_array_new ();
+  va_start (args, step1);
+  for (step = step1; step; step = va_arg (args, IdeAsyncStep))
+    g_ptr_array_add (funcs, step);
+  va_end (args);
+
+  task = g_task_new (source_object, cancellable, callback, user_data);
+  g_task_set_task_data (task, funcs, (GDestroyNotify)g_ptr_array_unref);
+
+  step1 (source_object,
+         cancellable,
+         ide_async_helper_cb,
+         g_object_ref (task));
+}
diff --git a/libide/ide-async-helper.h b/libide/ide-async-helper.h
new file mode 100644
index 0000000..cf515c3
--- /dev/null
+++ b/libide/ide-async-helper.h
@@ -0,0 +1,40 @@
+/* ide-async-helper.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_ASYNC_HELPER_H
+#define IDE_ASYNC_HELPER_H
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+typedef void (*IdeAsyncStep) (gpointer             source_object,
+                              GCancellable        *cancellable,
+                              GAsyncReadyCallback  callback,
+                              gpointer             user_data);
+
+void ide_async_helper_run (gpointer             source_object,
+                           GCancellable        *cancellable,
+                           GAsyncReadyCallback  callback,
+                           gpointer             user_data,
+                           IdeAsyncStep         step1,
+                           ...);
+
+G_END_DECLS
+
+#endif /* IDE_ASYNC_HELPER_H */
diff --git a/libide/ide-buffer-iter.h b/libide/ide-buffer-iter.h
new file mode 100644
index 0000000..ddd016a
--- /dev/null
+++ b/libide/ide-buffer-iter.h
@@ -0,0 +1,40 @@
+/* ide-buffer-iter.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_BUFFER_ITER_H
+#define IDE_BUFFER_ITER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_BUFFER_ITER               (ide_buffer_iter_get_type ())
+#define IDE_BUFFER_ITER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_BUFFER_ITER, 
IdeBufferIter))
+#define IDE_IS_BUFFER_ITER(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_BUFFER_ITER))
+#define IDE_BUFFER_ITER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), IDE_TYPE_BUFFER_ITER, 
IdeBufferIterInterface))
+
+struct _IdeBufferIterInterface
+{
+  GTypeInterface parent;
+};
+
+GType ide_buffer_iter_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_BUFFER_ITER_H */
diff --git a/libide/ide-buffer.h b/libide/ide-buffer.h
new file mode 100644
index 0000000..488c8ef
--- /dev/null
+++ b/libide/ide-buffer.h
@@ -0,0 +1,40 @@
+/* ide-buffer.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_BUFFER_H
+#define IDE_BUFFER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_BUFFER               (ide_buffer_get_type ())
+#define IDE_BUFFER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_BUFFER, IdeBuffer))
+#define IDE_IS_BUFFER(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_BUFFER))
+#define IDE_BUFFER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), IDE_TYPE_BUFFER, 
IdeBufferInterface))
+
+struct _IdeBufferInterface
+{
+  GTypeInterface parent;
+};
+
+GType ide_buffer_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_BUFFER_H */
diff --git a/libide/ide-build-result.c b/libide/ide-build-result.c
new file mode 100644
index 0000000..93ef6c3
--- /dev/null
+++ b/libide/ide-build-result.c
@@ -0,0 +1,336 @@
+/* ide-build-result.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gio/gunixoutputstream.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include "ide-build-result.h"
+
+typedef struct
+{
+  GMutex         mutex;
+
+  GInputStream  *stdout_reader;
+  GOutputStream *stdout_writer;
+
+  GInputStream  *stderr_reader;
+  GOutputStream *stderr_writer;
+} IdeBuildResultPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeBuildResult, ide_build_result, IDE_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  LAST_PROP
+};
+
+//static GParamSpec *gParamSpecs [LAST_PROP];
+
+static gboolean
+_ide_build_result_open_log (IdeBuildResult  *self,
+                            GInputStream   **read_stream,
+                            GOutputStream  **write_stream,
+                            const gchar     *template)
+{
+  g_autoptr(gchar) name_used = NULL;
+  gint fd;
+
+  g_return_val_if_fail (IDE_IS_BUILD_RESULT (self), FALSE);
+  g_return_val_if_fail (read_stream, FALSE);
+  g_return_val_if_fail (write_stream, FALSE);
+
+  fd = g_file_open_tmp (template, &name_used, NULL);
+
+  if (fd != -1)
+    {
+      g_autoptr(GFile) file;
+
+      file = g_file_new_for_path (name_used);
+      *read_stream = G_INPUT_STREAM (g_file_read (file, NULL, NULL));
+      *write_stream = g_unix_output_stream_new (fd, TRUE);
+      g_unlink (name_used);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+_ide_build_result_log (GOutputStream  *stream,
+                       const gchar    *message)
+{
+  g_autoptr(gchar) buffer;
+
+  g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
+  g_return_if_fail (message);
+
+  /*
+   * TODO: Is there a better way we can do this to just add a newline
+   *       at the end of stuff? Previously, I had a bunch of log stuff here
+   *       like date/time, but I didn't think it was necessary in the long
+   *       run. Would be nice to remove the printf as well. We need to do the
+   *       write as a single item in case we have multiple threads appending.
+   */
+
+  buffer = g_strdup_printf ("%s\n", message);
+  g_output_stream_write_all (stream, buffer, strlen (buffer),
+                             NULL, NULL, NULL);
+}
+
+void
+ide_build_result_log_stdout (IdeBuildResult *self,
+                             const gchar    *format,
+                             ...)
+{
+  IdeBuildResultPrivate *priv = ide_build_result_get_instance_private (self);
+  g_autoptr(gchar) msg = NULL;
+  va_list args;
+
+  /* lazy create stream if necessary */
+  (void)ide_build_result_get_stdout_stream (self);
+
+  if (priv->stdout_writer)
+    {
+      va_start (args, format);
+      msg = g_strdup_vprintf (format, args);
+      va_end (args);
+
+      _ide_build_result_log (priv->stdout_writer, msg);
+    }
+}
+
+void
+ide_build_result_log_stderr (IdeBuildResult *self,
+                             const gchar    *format,
+                             ...)
+{
+  IdeBuildResultPrivate *priv = ide_build_result_get_instance_private (self);
+  g_autoptr(gchar) msg = NULL;
+  va_list args;
+
+  /* lazy create stream if necessary */
+  (void)ide_build_result_get_stderr_stream (self);
+
+  if (priv->stderr_writer)
+    {
+      va_start (args, format);
+      msg = g_strdup_vprintf (format, args);
+      va_end (args);
+
+      _ide_build_result_log (priv->stderr_writer, msg);
+    }
+}
+
+/**
+ * ide_build_result_get_stderr_stream:
+ *
+ * Fetches a merged stdedrr stream for all child processes of this build result.
+ *
+ * Returns: (transfer none): A #GInputStream.
+ */
+GInputStream *
+ide_build_result_get_stderr_stream (IdeBuildResult *self)
+{
+  IdeBuildResultPrivate *priv = ide_build_result_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_BUILD_RESULT (self), NULL);
+
+  g_mutex_lock (&priv->mutex);
+
+  if (!priv->stderr_reader)
+    {
+      if (!_ide_build_result_open_log (self,
+                                       &priv->stderr_reader,
+                                       &priv->stderr_writer,
+                                       "libide-XXXXXX.stderr.log"))
+        g_warning (_("Failed to open stderr stream."));
+    }
+
+  g_mutex_unlock (&priv->mutex);
+
+  return priv->stderr_reader;
+}
+
+/**
+ * ide_build_result_get_stdout_stream:
+ *
+ * Fetches a merged stdout stream for all child processes of this build result.
+ *
+ * Returns: (transfer none) (nullable): A #GInputStream or %NULL.
+ */
+GInputStream *
+ide_build_result_get_stdout_stream (IdeBuildResult *self)
+{
+  IdeBuildResultPrivate *priv = ide_build_result_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_BUILD_RESULT (self), NULL);
+
+  g_mutex_lock (&priv->mutex);
+
+  if (!priv->stdout_reader)
+    {
+      if (!_ide_build_result_open_log (self,
+                                       &priv->stdout_reader,
+                                       &priv->stdout_writer,
+                                       "libide-XXXXXX.stdout.log"))
+        g_warning (_("Failed to open stdout stream."));
+    }
+
+  g_mutex_unlock (&priv->mutex);
+
+  return priv->stdout_reader;
+}
+
+static void
+ide_build_result_tail_cb (GObject      *object,
+                          GAsyncResult *result,
+                          gpointer      user_data)
+{
+  GDataInputStream *reader = (GDataInputStream *)object;
+  g_autoptr(GOutputStream) writer = user_data;
+  g_autoptr(gchar) line = NULL;
+  g_autoptr(GError) error = NULL;
+  gsize n_read;
+
+  g_return_if_fail (G_IS_INPUT_STREAM (reader));
+  g_return_if_fail (G_IS_OUTPUT_STREAM (writer));
+
+  line = g_data_input_stream_read_line_finish_utf8 (reader, result, &n_read,
+                                                    &error);
+
+  if (line)
+    {
+      _ide_build_result_log (writer, line);
+      g_data_input_stream_read_line_async (reader,
+                                           G_PRIORITY_DEFAULT,
+                                           NULL,
+                                           ide_build_result_tail_cb,
+                                           g_object_ref (writer));
+    }
+}
+
+static void
+ide_build_result_tail_into (IdeBuildResult *self,
+                            GInputStream   *reader,
+                            GOutputStream  *writer)
+{
+  g_autoptr(GDataInputStream) data_reader = NULL;
+
+  g_return_if_fail (IDE_IS_BUILD_RESULT (self));
+  g_return_if_fail (G_IS_INPUT_STREAM (reader));
+  g_return_if_fail (G_IS_OUTPUT_STREAM (writer));
+
+  data_reader = g_data_input_stream_new (reader);
+
+  g_data_input_stream_read_line_async (data_reader,
+                                       G_PRIORITY_DEFAULT,
+                                       NULL,
+                                       ide_build_result_tail_cb,
+                                       g_object_ref (writer));
+}
+
+void
+ide_build_result_log_subprocess (IdeBuildResult *self,
+                                 GSubprocess    *subprocess)
+{
+  IdeBuildResultPrivate *priv = ide_build_result_get_instance_private (self);
+  GInputStream *stdout_stream;
+  GInputStream *stderr_stream;
+
+  g_return_if_fail (IDE_IS_BUILD_RESULT (self));
+  g_return_if_fail (G_IS_SUBPROCESS (subprocess));
+
+  /* ensure lazily created streams are available */
+  (void)ide_build_result_get_stderr_stream (self);
+  (void)ide_build_result_get_stdout_stream (self);
+
+  stderr_stream = g_subprocess_get_stderr_pipe (subprocess);
+  if (stderr_stream)
+    ide_build_result_tail_into (self, stderr_stream, priv->stderr_writer);
+
+  stdout_stream = g_subprocess_get_stdout_pipe (subprocess);
+  if (stdout_stream)
+    ide_build_result_tail_into (self, stdout_stream, priv->stdout_writer);
+}
+
+static void
+ide_build_result_finalize (GObject *object)
+{
+  IdeBuildResult *self = (IdeBuildResult *)object;
+  IdeBuildResultPrivate *priv = ide_build_result_get_instance_private (self);
+
+  g_mutex_clear (&priv->mutex);
+
+  g_clear_object (&priv->stderr_reader);
+  g_clear_object (&priv->stderr_writer);
+
+  g_clear_object (&priv->stdout_reader);
+  g_clear_object (&priv->stdout_writer);
+
+  G_OBJECT_CLASS (ide_build_result_parent_class)->finalize (object);
+}
+
+static void
+ide_build_result_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  //IdeBuildResult *self = IDE_BUILD_RESULT (object);
+
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_build_result_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  //IdeBuildResult *self = IDE_BUILD_RESULT (object);
+
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_build_result_class_init (IdeBuildResultClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_build_result_finalize;
+  object_class->get_property = ide_build_result_get_property;
+  object_class->set_property = ide_build_result_set_property;
+}
+
+static void
+ide_build_result_init (IdeBuildResult *self)
+{
+  IdeBuildResultPrivate *priv = ide_build_result_get_instance_private (self);
+
+  g_mutex_init (&priv->mutex);
+}
diff --git a/libide/ide-build-result.h b/libide/ide-build-result.h
new file mode 100644
index 0000000..0586673
--- /dev/null
+++ b/libide/ide-build-result.h
@@ -0,0 +1,51 @@
+/* ide-build-result.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_BUILD_RESULT_H
+#define IDE_BUILD_RESULT_H
+
+#include <gio/gio.h>
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_BUILD_RESULT (ide_build_result_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeBuildResult, ide_build_result, IDE, BUILD_RESULT,
+                          IdeObject)
+
+struct _IdeBuildResultClass
+{
+  IdeObjectClass parent;
+};
+
+GInputStream  *ide_build_result_get_stdout_stream (IdeBuildResult *result);
+GInputStream  *ide_build_result_get_stderr_stream (IdeBuildResult *result);
+void           ide_build_result_log_subprocess    (IdeBuildResult *result,
+                                                   GSubprocess    *subprocess);
+void           ide_build_result_log_stdout        (IdeBuildResult *result,
+                                                   const gchar    *format,
+                                                   ...) G_GNUC_PRINTF (2, 3);
+void           ide_build_result_log_stderr        (IdeBuildResult *result,
+                                                   const gchar    *format,
+                                                   ...) G_GNUC_PRINTF (2, 3);
+
+G_END_DECLS
+
+#endif /* IDE_BUILD_RESULT_H */
diff --git a/libide/ide-build-system.c b/libide/ide-build-system.c
new file mode 100644
index 0000000..706ba3d
--- /dev/null
+++ b/libide/ide-build-system.c
@@ -0,0 +1,240 @@
+/* ide-build-system.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-build-system.h"
+#include "ide-context.h"
+#include "ide-device.h"
+
+typedef struct
+{
+  GFile *project_file;
+} IdeBuildSystemPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeBuildSystem, ide_build_system, IDE_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_PROJECT_FILE,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+GFile *
+ide_build_system_get_project_file (IdeBuildSystem *system)
+{
+  IdeBuildSystemPrivate *priv = ide_build_system_get_instance_private (system);
+
+  g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (system), NULL);
+
+  return priv->project_file;
+}
+
+static void
+ide_build_system_set_project_file (IdeBuildSystem *system,
+                                   GFile          *project_file)
+{
+  IdeBuildSystemPrivate *priv = ide_build_system_get_instance_private (system);
+
+  g_return_if_fail (IDE_IS_BUILD_SYSTEM (system));
+  g_return_if_fail (G_IS_FILE (project_file));
+
+  if (project_file != priv->project_file)
+    {
+      g_clear_object (&priv->project_file);
+      priv->project_file = g_object_ref (project_file);
+      g_object_notify_by_pspec (G_OBJECT (system),
+                                gParamSpecs [PROP_PROJECT_FILE]);
+    }
+}
+
+static void
+ide_build_system_finalize (GObject *object)
+{
+  IdeBuildSystem *self = (IdeBuildSystem *)object;
+  IdeBuildSystemPrivate *priv = ide_build_system_get_instance_private (self);
+
+  g_clear_object (&priv->project_file);
+
+  G_OBJECT_CLASS (ide_build_system_parent_class)->finalize (object);
+}
+
+static void
+ide_build_system_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  IdeBuildSystem *self = IDE_BUILD_SYSTEM (object);
+
+  switch (prop_id)
+    {
+    case PROP_PROJECT_FILE:
+      g_value_set_object (value, ide_build_system_get_project_file (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_build_system_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  IdeBuildSystem *self = IDE_BUILD_SYSTEM (object);
+
+  switch (prop_id)
+    {
+    case PROP_PROJECT_FILE:
+      ide_build_system_set_project_file (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_build_system_class_init (IdeBuildSystemClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_build_system_finalize;
+  object_class->get_property = ide_build_system_get_property;
+  object_class->set_property = ide_build_system_set_property;
+
+  gParamSpecs [PROP_PROJECT_FILE] =
+    g_param_spec_object ("project-file",
+                         _("Project File"),
+                         _("The project file."),
+                         G_TYPE_FILE,
+                         (G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_PROJECT_FILE,
+                                   gParamSpecs [PROP_PROJECT_FILE]);
+}
+
+static void
+ide_build_system_init (IdeBuildSystem *self)
+{
+}
+
+/**
+ * ide_build_system_new_async:
+ * @context: #IdeBuildSystem
+ * @project_file: A #GFile containing the directory or project file.
+ * @cancellable: (allow-none): A #GCancellable
+ * @callback: A callback to execute upon completion
+ * @user_data: User data for @callback.
+ *
+ * Asynchronously creates a new #IdeBuildSystem instance using the registered
+ * #GIOExtensionPoint system. Each extension point will be tried asynchronously
+ * by priority until one has been found that supports @project_file.
+ *
+ * If no build system could be found, then ide_build_system_new_finish() will
+ * return %NULL.
+ */
+void
+ide_build_system_new_async (IdeContext          *context,
+                            GFile               *project_file,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback  callback,
+                            gpointer             user_data)
+{
+  g_return_if_fail (IDE_IS_CONTEXT (context));
+  g_return_if_fail (G_IS_FILE (project_file));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+  g_return_if_fail (callback);
+
+  ide_object_new_async (IDE_BUILD_SYSTEM_EXTENSION_POINT,
+                        G_PRIORITY_DEFAULT,
+                        cancellable,
+                        callback,
+                        user_data,
+                        "context", context,
+                        "project-file", project_file,
+                        NULL);
+}
+
+/**
+ * ide_build_system_new_finish:
+ *
+ * Complete an asynchronous call to ide_build_system_new_async().
+ *
+ * Returns: (transfer full): An #IdeBuildSystem if successful; otherwise
+ *   %NULL and @error is set.
+ */
+IdeBuildSystem *
+ide_build_system_new_finish (GAsyncResult  *result,
+                             GError       **error)
+{
+  IdeObject *ret;
+
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+
+  ret = ide_object_new_finish (result, error);
+
+  return IDE_BUILD_SYSTEM (ret);
+}
+
+/**
+ * ide_build_system_get_builder:
+ * @system: The #IdeBuildSystem to perform the build.
+ * @config: The configuration options for the build.
+ * @device: The #IdeDevice the result should be able to run on.
+ *
+ * This function should return an #IdeBuilder that can be used to perform a
+ * build of the project using the configuration specified. @device may be
+ * a non-local device, for which cross-compilation may be necessary.
+ *
+ * Returns: (transfer full): An #IdeBuilder or %NULL and @error is set.
+ */
+IdeBuilder *
+ide_build_system_get_builder (IdeBuildSystem  *system,
+                              GKeyFile        *config,
+                              IdeDevice       *device,
+                              GError         **error)
+{
+  IdeBuildSystemClass *klass;
+  IdeBuilder *ret = NULL;
+
+  g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (system), NULL);
+  g_return_val_if_fail (config, NULL);
+  g_return_val_if_fail (IDE_IS_DEVICE (device), NULL);
+
+  klass = IDE_BUILD_SYSTEM_GET_CLASS (system);
+
+  if (klass->get_builder)
+    ret = klass->get_builder (system, config, device, error);
+  else
+    g_set_error (error,
+                 G_IO_ERROR,
+                 G_IO_ERROR_NOT_SUPPORTED,
+                 _("%s() is not supported on %s build system."),
+                 G_STRFUNC,
+                 g_type_name (G_TYPE_FROM_INSTANCE (system)));
+
+  return ret;
+}
+
diff --git a/libide/ide-build-system.h b/libide/ide-build-system.h
new file mode 100644
index 0000000..d9de9d8
--- /dev/null
+++ b/libide/ide-build-system.h
@@ -0,0 +1,58 @@
+/* ide-build-system.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_BUILD_SYSTEM_H
+#define IDE_BUILD_SYSTEM_H
+
+#include <gio/gio.h>
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_BUILD_SYSTEM            (ide_build_system_get_type())
+#define IDE_BUILD_SYSTEM_EXTENSION_POINT "org.gnome.libide.extensions.build-system"
+
+G_DECLARE_DERIVABLE_TYPE (IdeBuildSystem, ide_build_system,
+                          IDE, BUILD_SYSTEM, IdeObject)
+
+struct _IdeBuildSystemClass
+{
+  IdeObjectClass parent;
+
+  IdeBuilder *(*get_builder) (IdeBuildSystem  *system,
+                              GKeyFile        *config,
+                              IdeDevice       *device,
+                              GError         **error);
+};
+
+void            ide_build_system_new_async   (IdeContext           *context,
+                                              GFile                *project_file,
+                                              GCancellable         *cancellable,
+                                              GAsyncReadyCallback   callback,
+                                              gpointer              user_data);
+IdeBuildSystem *ide_build_system_new_finish  (GAsyncResult         *result,
+                                              GError              **error);
+IdeBuilder     *ide_build_system_get_builder (IdeBuildSystem       *system,
+                                              GKeyFile             *config,
+                                              IdeDevice            *device,
+                                              GError              **error);
+
+G_END_DECLS
+
+#endif /* IDE_BUILD_SYSTEM_H */
diff --git a/libide/ide-builder.c b/libide/ide-builder.c
new file mode 100644
index 0000000..3378bf5
--- /dev/null
+++ b/libide/ide-builder.c
@@ -0,0 +1,145 @@
+/* ide-builder.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-build-result.h"
+#include "ide-builder.h"
+
+typedef struct
+{
+  void *foo;
+} IdeBuilderPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeBuilder, ide_builder, IDE_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  LAST_PROP
+};
+
+//static GParamSpec *gParamSpecs [LAST_PROP];
+
+void
+ide_builder_build_async (IdeBuilder           *builder,
+                         IdeBuildResult      **result,
+                         GCancellable         *cancellable,
+                         GAsyncReadyCallback   callback,
+                         gpointer              user_data)
+{
+  IdeBuilderClass *klass;
+
+  g_return_if_fail (IDE_IS_BUILDER (builder));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  if (result)
+    *result = NULL;
+
+  klass = IDE_BUILDER_GET_CLASS (builder);
+
+  if (klass->build_async)
+    {
+      klass->build_async (builder, result, cancellable, callback, user_data);
+      return;
+    }
+
+  g_warning (_("%s does not implement build_async()"),
+             g_type_name (G_TYPE_FROM_INSTANCE (builder)));
+
+  g_task_report_new_error (builder, callback, user_data,
+                           ide_builder_build_async,
+                           G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                           _("No implementation of build_async()"));
+}
+
+IdeBuildResult *
+ide_builder_build_finish (IdeBuilder    *builder,
+                          GAsyncResult  *result,
+                          GError       **error)
+{
+  IdeBuilderClass *klass;
+  IdeBuildResult *ret = NULL;
+
+  g_return_val_if_fail (IDE_IS_BUILDER (builder), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+
+  klass = IDE_BUILDER_GET_CLASS (builder);
+
+  if (klass->build_finish)
+    ret = klass->build_finish (builder, result, error);
+  else if (G_IS_TASK (result))
+    ret = g_task_propagate_pointer (G_TASK (result), error);
+
+  g_return_val_if_fail (!ret || IDE_IS_BUILD_RESULT (ret), NULL);
+
+  return ret;
+}
+
+static void
+ide_builder_finalize (GObject *object)
+{
+  //IdeBuilder *self = (IdeBuilder *)object;
+  //IdeBuilderPrivate *priv = ide_builder_get_instance_private (self);
+
+  G_OBJECT_CLASS (ide_builder_parent_class)->finalize (object);
+}
+
+static void
+ide_builder_get_property (GObject    *object,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  //IdeBuilder *self = IDE_BUILDER (object);
+
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_builder_set_property (GObject      *object,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  //IdeBuilder *self = IDE_BUILDER (object);
+
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_builder_class_init (IdeBuilderClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_builder_finalize;
+  object_class->get_property = ide_builder_get_property;
+  object_class->set_property = ide_builder_set_property;
+}
+
+static void
+ide_builder_init (IdeBuilder *self)
+{
+}
diff --git a/libide/ide-builder.h b/libide/ide-builder.h
new file mode 100644
index 0000000..dc5cb56
--- /dev/null
+++ b/libide/ide-builder.h
@@ -0,0 +1,55 @@
+/* ide-builder.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_BUILDER_H
+#define IDE_BUILDER_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_BUILDER (ide_builder_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeBuilder, ide_builder, IDE, BUILDER, IdeObject)
+
+struct _IdeBuilderClass
+{
+  GObjectClass parent;
+
+  void            (*build_async)  (IdeBuilder           *builder,
+                                   IdeBuildResult      **result,
+                                   GCancellable         *cancellable,
+                                   GAsyncReadyCallback   callback,
+                                   gpointer              user_data);
+  IdeBuildResult *(*build_finish) (IdeBuilder           *builder,
+                                   GAsyncResult         *result,
+                                   GError              **error);
+};
+
+void            ide_builder_build_async  (IdeBuilder           *builder,
+                                          IdeBuildResult      **result,
+                                          GCancellable         *cancellable,
+                                          GAsyncReadyCallback   callback,
+                                          gpointer              user_data);
+IdeBuildResult *ide_builder_build_finish (IdeBuilder           *builder,
+                                          GAsyncResult         *result,
+                                          GError              **error);
+
+G_END_DECLS
+
+#endif /* IDE_BUILDER_H */
diff --git a/libide/ide-context.c b/libide/ide-context.c
new file mode 100644
index 0000000..4a654db
--- /dev/null
+++ b/libide/ide-context.c
@@ -0,0 +1,775 @@
+/* ide-context.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-context"
+
+#include <glib/gi18n.h>
+
+#include "ide-async-helper.h"
+#include "ide-build-system.h"
+#include "ide-context.h"
+#include "ide-device-manager.h"
+#include "ide-project.h"
+#include "ide-service.h"
+#include "ide-unsaved-files.h"
+#include "ide-vcs.h"
+
+typedef struct
+{
+  IdeBuildSystem   *build_system;
+  IdeDeviceManager *device_manager;
+  IdeProject       *project;
+  GFile            *project_file;
+  gchar            *root_build_dir;
+  GHashTable       *services;
+  IdeUnsavedFiles  *unsaved_files;
+  IdeVcs           *vcs;
+} IdeContextPrivate;
+
+static void async_initable_init (GAsyncInitableIface *);
+
+G_DEFINE_TYPE_EXTENDED (IdeContext, ide_context, G_TYPE_OBJECT, 0,
+                        G_ADD_PRIVATE (IdeContext)
+                        G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
+                                               async_initable_init))
+
+enum {
+  PROP_0,
+  PROP_BUILD_SYSTEM,
+  PROP_DEVICE_MANAGER,
+  PROP_PROJECT_FILE,
+  PROP_ROOT_BUILD_DIR,
+  PROP_VCS,
+  PROP_UNSAVED_FILES,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+/**
+ * ide_context_get_build_system:
+ *
+ * Fetches the "build-system" property of @context.
+ *
+ * Returns: (transfer none): An #IdeBuildSystem.
+ */
+IdeBuildSystem *
+ide_context_get_build_system (IdeContext *context)
+{
+  IdeContextPrivate *priv = ide_context_get_instance_private (context);
+
+  g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
+
+  return priv->build_system;
+}
+
+/**
+ * ide_context_get_device_manager:
+ *
+ * Retrieves the "device-manager" property. The device manager is responsible
+ * for connecting and disconnecting to physical or virtual devices within
+ * LibIDE.
+ *
+ * Returns: (transfer none): An #IdeDeviceManager.
+ */
+IdeDeviceManager *
+ide_context_get_device_manager (IdeContext *context)
+{
+  IdeContextPrivate *priv = ide_context_get_instance_private (context);
+
+  g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
+
+  return priv->device_manager;
+}
+
+/**
+ * ide_context_get_root_build_dir:
+ *
+ * Retrieves the "root-build-dir" for the context. This is the root directory
+ * that will contain builds made for various devices.
+ *
+ * Returns: A string containing the "root-build-dir" property.
+ */
+const gchar *
+ide_context_get_root_build_dir (IdeContext *context)
+{
+  IdeContextPrivate *priv = ide_context_get_instance_private (context);
+
+  g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
+
+  return priv->root_build_dir;
+}
+
+/**
+ * ide_context_set_root_build_dir:
+ * @root_build_dir: the path to the root build directory.
+ *
+ * Sets the "root-build-dir" property. This is the root directory that will
+ * be used when building projects for projects that support building out of
+ * tree.
+ */
+void
+ide_context_set_root_build_dir (IdeContext  *context,
+                                const gchar *root_build_dir)
+{
+  IdeContextPrivate *priv = ide_context_get_instance_private (context);
+
+  g_return_if_fail (IDE_IS_CONTEXT (context));
+  g_return_if_fail (root_build_dir);
+
+  if (priv->root_build_dir != root_build_dir)
+    {
+      g_free (priv->root_build_dir);
+      priv->root_build_dir = g_strdup (root_build_dir);
+      g_object_notify_by_pspec (G_OBJECT (context),
+                                gParamSpecs [PROP_ROOT_BUILD_DIR]);
+    }
+}
+
+/**
+ * ide_context_get_unsaved_files:
+ *
+ * Returns the unsaved files for the #IdeContext. These are the contents of
+ * open buffers in the IDE.
+ *
+ * Returns: (transfer none): An #IdeUnsavedFiles.
+ */
+IdeUnsavedFiles *
+ide_context_get_unsaved_files (IdeContext *context)
+{
+  IdeContextPrivate *priv = ide_context_get_instance_private (context);
+
+  g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
+
+  return priv->unsaved_files;
+}
+
+IdeVcs *
+ide_context_get_vcs (IdeContext *context)
+{
+  IdeContextPrivate *priv = ide_context_get_instance_private (context);
+
+  g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
+
+  return priv->vcs;
+}
+
+static void
+ide_context_new_cb (GObject      *object,
+                    GAsyncResult *result,
+                    gpointer      user_data)
+{
+  GAsyncInitable *initable = (GAsyncInitable *)object;
+  GError *error = NULL;
+  GTask *task = user_data;
+
+  g_return_if_fail (G_IS_ASYNC_INITABLE (initable));
+  g_return_if_fail (G_IS_TASK (task));
+
+  object = g_async_initable_new_finish (initable, result, &error);
+
+  if (!object)
+    g_task_return_error (task, error);
+  else
+    g_task_return_pointer (task, object, g_object_unref);
+
+  g_object_unref (task);
+}
+
+void
+ide_context_new_async (GFile               *project_file,
+                       GCancellable        *cancellable,
+                       GAsyncReadyCallback  callback,
+                       gpointer             user_data)
+{
+  GTask *task;
+
+  g_return_if_fail (G_IS_FILE (project_file));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (NULL, cancellable, callback, user_data);
+  g_async_initable_new_async (IDE_TYPE_CONTEXT,
+                              G_PRIORITY_DEFAULT,
+                              cancellable,
+                              ide_context_new_cb,
+                              g_object_ref (task),
+                              "project-file", project_file,
+                              NULL);
+  g_object_unref (task);
+}
+
+IdeContext *
+ide_context_new_finish (GAsyncResult  *result,
+                        GError       **error)
+{
+  GTask *task = (GTask *)result;
+
+  g_return_val_if_fail (G_IS_TASK (task), NULL);
+
+  return g_task_propagate_pointer (task, error);
+}
+
+IdeProject *
+ide_context_get_project (IdeContext *context)
+{
+  IdeContextPrivate *priv = ide_context_get_instance_private (context);
+
+  g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
+
+  return priv->project;
+}
+
+GFile *
+ide_context_get_project_file (IdeContext *context)
+{
+  IdeContextPrivate *priv = ide_context_get_instance_private (context);
+
+  g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
+
+  return priv->project_file;
+}
+
+static void
+ide_context_set_project_file (IdeContext *context,
+                              GFile      *project_file)
+{
+  IdeContextPrivate *priv = ide_context_get_instance_private (context);
+
+  g_return_if_fail (IDE_IS_CONTEXT (context));
+
+  if (project_file != priv->project_file)
+    {
+      g_clear_object (&priv->project_file);
+      if (project_file)
+        priv->project_file = g_object_ref (project_file);
+      g_object_notify_by_pspec (G_OBJECT (context),
+                                gParamSpecs [PROP_PROJECT_FILE]);
+    }
+}
+
+static gpointer
+ide_context_create_service (IdeContext *context,
+                            GType       service_type)
+{
+  IdeContextPrivate *priv = ide_context_get_instance_private (context);
+  IdeService *service;
+
+  g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
+  g_return_val_if_fail (g_type_is_a (service_type, IDE_TYPE_SERVICE), NULL);
+
+  service = g_object_new (service_type,
+                          "context", context,
+                          NULL);
+
+  ide_service_start (service);
+
+  g_hash_table_insert (priv->services,
+                       GINT_TO_POINTER (service_type),
+                       service);
+
+  return service;
+}
+
+/**
+ * ide_context_get_service_typed:
+ * @service_type: A #GType of the service desired.
+ *
+ * Retrieves a service matching @service_type. If no match was found, a type
+ * implementing the requested service type will be returned. If no matching
+ * service type could be found, then an instance of the service will be
+ * created, started, and returned.
+ *
+ * Returns: (transfer none) (nullable): An #IdeService or %NULL.
+ */
+gpointer
+ide_context_get_service_typed (IdeContext *context,
+                               GType       service_type)
+{
+  IdeContextPrivate *priv = ide_context_get_instance_private (context);
+  IdeService *service;
+  GHashTableIter iter;
+  gpointer key;
+  gpointer value;
+
+  g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
+  g_return_val_if_fail (g_type_is_a (service_type, IDE_TYPE_SERVICE), NULL);
+
+  service = g_hash_table_lookup (priv->services,
+                                 GINT_TO_POINTER (service_type));
+
+  if (service)
+    return service;
+
+  g_hash_table_iter_init (&iter, priv->services);
+
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      service = value;
+
+      if (g_type_is_a (G_TYPE_FROM_INSTANCE (service), service_type))
+        return service;
+    }
+
+  if (!service)
+    service = ide_context_create_service (context, service_type);
+
+  return service;
+}
+
+static void
+ide_context_dispose (GObject *object)
+{
+  IdeContext *self = (IdeContext *)object;
+  IdeContextPrivate *priv = ide_context_get_instance_private (self);
+  GHashTableIter iter;
+  gpointer key;
+  gpointer value;
+
+  g_return_if_fail (IDE_IS_CONTEXT (self));
+
+  g_hash_table_iter_init (&iter, priv->services);
+
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      IdeService *service = value;
+
+      g_assert (IDE_IS_SERVICE (service));
+
+      if (ide_service_get_running (service))
+        ide_service_stop (service);
+    }
+
+  G_OBJECT_CLASS (ide_context_parent_class)->dispose (object);
+}
+
+static void
+ide_context_finalize (GObject *object)
+{
+  IdeContext *self = (IdeContext *)object;
+  IdeContextPrivate *priv = ide_context_get_instance_private (self);
+
+  g_clear_pointer (&priv->services, g_hash_table_unref);
+  g_clear_pointer (&priv->root_build_dir, g_free);
+
+  g_clear_object (&priv->build_system);
+  g_clear_object (&priv->device_manager);
+  g_clear_object (&priv->project);
+  g_clear_object (&priv->project_file);
+  g_clear_object (&priv->unsaved_files);
+  g_clear_object (&priv->vcs);
+
+  G_OBJECT_CLASS (ide_context_parent_class)->finalize (object);
+}
+
+static void
+ide_context_get_property (GObject    *object,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  IdeContext *self = IDE_CONTEXT (object);
+
+  switch (prop_id)
+    {
+    case PROP_BUILD_SYSTEM:
+      g_value_set_object (value, ide_context_get_build_system (self));
+      break;
+
+    case PROP_DEVICE_MANAGER:
+      g_value_set_object (value, ide_context_get_device_manager (self));
+      break;
+
+    case PROP_PROJECT_FILE:
+      g_value_set_object (value, ide_context_get_project_file (self));
+      break;
+
+    case PROP_ROOT_BUILD_DIR:
+      g_value_set_string (value, ide_context_get_root_build_dir (self));
+      break;
+
+    case PROP_UNSAVED_FILES:
+      g_value_set_object (value, ide_context_get_unsaved_files (self));
+      break;
+
+    case PROP_VCS:
+      g_value_set_object (value, ide_context_get_vcs (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_context_set_property (GObject      *object,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  IdeContext *self = IDE_CONTEXT (object);
+
+  switch (prop_id)
+    {
+    case PROP_PROJECT_FILE:
+      ide_context_set_project_file (self, g_value_get_object (value));
+      break;
+
+    case PROP_ROOT_BUILD_DIR:
+      ide_context_set_root_build_dir (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_context_class_init (IdeContextClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = ide_context_dispose;
+  object_class->finalize = ide_context_finalize;
+  object_class->get_property = ide_context_get_property;
+  object_class->set_property = ide_context_set_property;
+
+  gParamSpecs [PROP_BUILD_SYSTEM] =
+    g_param_spec_object ("build-system",
+                         _("Build System"),
+                         _("The build system used by the context."),
+                         IDE_TYPE_BUILD_SYSTEM,
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_BUILD_SYSTEM,
+                                   gParamSpecs [PROP_BUILD_SYSTEM]);
+
+  gParamSpecs [PROP_DEVICE_MANAGER] =
+    g_param_spec_object ("device-manager",
+                         _("Device Manager"),
+                         _("The device manager for the context."),
+                         IDE_TYPE_DEVICE_MANAGER,
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_DEVICE_MANAGER,
+                                   gParamSpecs [PROP_DEVICE_MANAGER]);
+
+  gParamSpecs [PROP_PROJECT_FILE] =
+    g_param_spec_object ("project-file",
+                         _("Project File"),
+                         _("The project file for the context."),
+                         G_TYPE_FILE,
+                         (G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_PROJECT_FILE,
+                                   gParamSpecs [PROP_PROJECT_FILE]);
+
+  gParamSpecs [PROP_ROOT_BUILD_DIR] =
+    g_param_spec_string ("root-build-dir",
+                         _("Root Build Dir"),
+                         _("The root directory to perform builds within."),
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_ROOT_BUILD_DIR,
+                                   gParamSpecs [PROP_ROOT_BUILD_DIR]);
+
+  gParamSpecs [PROP_UNSAVED_FILES] =
+    g_param_spec_object ("unsaved-files",
+                         _("Unsaved Files"),
+                         _("The unsaved files in the context."),
+                         IDE_TYPE_UNSAVED_FILES,
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_UNSAVED_FILES,
+                                   gParamSpecs [PROP_UNSAVED_FILES]);
+
+  gParamSpecs [PROP_VCS] =
+    g_param_spec_object ("vcs",
+                         _("Vcs"),
+                         _("The vcs for the context."),
+                         IDE_TYPE_VCS,
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_VCS,
+                                   gParamSpecs [PROP_VCS]);
+}
+
+static void
+ide_context_init (IdeContext *self)
+{
+  IdeContextPrivate *priv = ide_context_get_instance_private (self);
+
+  priv->root_build_dir = g_build_filename (g_get_user_cache_dir (),
+                                           "libide", "builds", NULL);
+
+  priv->device_manager = g_object_new (IDE_TYPE_DEVICE_MANAGER,
+                                       "context", self,
+                                       NULL);
+
+  priv->project = g_object_new (IDE_TYPE_PROJECT,
+                                "context", self,
+                                NULL);
+
+  priv->services = g_hash_table_new_full (g_direct_hash,
+                                          g_direct_equal,
+                                          NULL,
+                                          g_object_unref);
+
+  priv->unsaved_files = g_object_new (IDE_TYPE_UNSAVED_FILES,
+                                      "context", self,
+                                      NULL);
+}
+
+static void
+ide_context_init_project_name_cb (GObject      *object,
+                                  GAsyncResult *result,
+                                  gpointer      user_data)
+{
+  IdeContextPrivate *priv;
+  IdeContext *context;
+  g_autoptr(gchar) name = NULL;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GFileInfo) file_info = NULL;
+  GFile *file = (GFile *)object;
+
+  g_return_if_fail (G_IS_FILE (file));
+  g_return_if_fail (G_IS_TASK (task));
+
+  context = g_task_get_source_object (task);
+  priv = ide_context_get_instance_private (context);
+
+  file_info = g_file_query_info_finish (file, result, NULL);
+
+  if (file_info &&
+      (G_FILE_TYPE_DIRECTORY == g_file_info_get_file_type (file_info)))
+    {
+      g_autoptr(gchar) name;
+
+      name = g_file_get_basename (file);
+      ide_project_set_name (priv->project, name);
+    }
+  else
+    {
+      g_autoptr(GFile) parent;
+      g_autoptr(gchar) name;
+
+      parent = g_file_get_parent (file);
+      name = g_file_get_basename (parent);
+
+      ide_project_set_name (priv->project, name);
+    }
+
+  g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_context_init_project_name (gpointer             source_object,
+                               GCancellable        *cancellable,
+                               GAsyncReadyCallback  callback,
+                               gpointer             user_data)
+{
+  IdeContext *context = source_object;
+  IdeContextPrivate *priv = ide_context_get_instance_private (context);
+  g_autoptr(GTask) task = NULL;
+
+  g_return_if_fail (IDE_IS_CONTEXT (context));
+
+  task = g_task_new (source_object, cancellable, callback, user_data);
+
+  if (!ide_project_get_name (priv->project))
+    g_file_query_info_async (priv->project_file,
+                             G_FILE_ATTRIBUTE_STANDARD_TYPE,
+                             G_FILE_QUERY_INFO_NONE,
+                             G_PRIORITY_DEFAULT,
+                             g_task_get_cancellable (task),
+                             ide_context_init_project_name_cb,
+                             g_object_ref (task));
+  else
+    g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_context_init_vcs_cb (GObject      *object,
+                         GAsyncResult *result,
+                         gpointer      user_data)
+{
+  IdeContextPrivate *priv;
+  IdeContext *self;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(IdeVcs) vcs = NULL;
+  GError *error = NULL;
+
+  g_return_if_fail (G_IS_ASYNC_RESULT (result));
+  g_return_if_fail (G_IS_TASK (task));
+
+  self = g_task_get_source_object (task);
+  priv = ide_context_get_instance_private (self);
+
+  if (!(vcs = ide_vcs_new_finish (result, &error)))
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  priv->vcs = g_object_ref (vcs);
+
+  g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_context_init_vcs (gpointer             source_object,
+                      GCancellable        *cancellable,
+                      GAsyncReadyCallback  callback,
+                      gpointer             user_data)
+{
+  IdeContext *context = source_object;
+  g_autoptr(GTask) task = NULL;
+
+  g_return_if_fail (IDE_IS_CONTEXT (context));
+
+  task = g_task_new (source_object, cancellable, callback, user_data);
+
+  ide_vcs_new_async (context,
+                     G_PRIORITY_DEFAULT,
+                     cancellable,
+                     ide_context_init_vcs_cb,
+                     g_object_ref (task));
+}
+
+static void
+ide_context_init_build_system_cb (GObject      *object,
+                                  GAsyncResult *result,
+                                  gpointer      user_data)
+{
+  g_autoptr(IdeBuildSystem) build_system = NULL;
+  g_autoptr(GTask) task = user_data;
+  IdeContextPrivate *priv;
+  IdeContext *self;
+  GError *error = NULL;
+
+  self = g_task_get_source_object (task);
+  priv = ide_context_get_instance_private (self);
+
+  if (!(build_system = ide_build_system_new_finish (result, &error)))
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  priv->build_system = g_object_ref (build_system);
+
+  g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_context_init_build_system (gpointer             source_object,
+                               GCancellable        *cancellable,
+                               GAsyncReadyCallback  callback,
+                               gpointer             user_data)
+{
+  IdeContext *self = source_object;
+  IdeContextPrivate *priv = ide_context_get_instance_private (self);
+  g_autoptr(GTask) task = NULL;
+
+  g_return_if_fail (IDE_IS_CONTEXT (self));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  ide_build_system_new_async (self,
+                              priv->project_file,
+                              cancellable,
+                              ide_context_init_build_system_cb,
+                              g_object_ref (task));
+}
+
+static void
+ide_context_init_unsaved_files_cb (GObject      *object,
+                                   GAsyncResult *result,
+                                   gpointer      user_data)
+{
+  IdeUnsavedFiles *unsaved_files = (IdeUnsavedFiles *)object;
+  g_autoptr(GTask) task = user_data;
+  GError *error = NULL;
+
+  g_return_if_fail (IDE_IS_UNSAVED_FILES (unsaved_files));
+
+  if (!ide_unsaved_files_restore_finish (unsaved_files, result, &error))
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_context_init_unsaved_files (gpointer             source_object,
+                                GCancellable        *cancellable,
+                                GAsyncReadyCallback  callback,
+                                gpointer             user_data)
+{
+  IdeContext *self = source_object;
+  IdeContextPrivate *priv = ide_context_get_instance_private (self);
+  g_autoptr(GTask) task = NULL;
+
+  g_return_if_fail (IDE_IS_CONTEXT (self));
+
+  task = g_task_new (source_object, cancellable, callback, user_data);
+  ide_unsaved_files_restore_async (priv->unsaved_files,
+                                   cancellable,
+                                   ide_context_init_unsaved_files_cb,
+                                   g_object_ref (task));
+}
+
+static void
+ide_context_init_async (GAsyncInitable      *initable,
+                        int                  io_priority,
+                        GCancellable        *cancellable,
+                        GAsyncReadyCallback  callback,
+                        gpointer             user_data)
+{
+  IdeContext *context = (IdeContext *)initable;
+  g_autoptr(GTask) task = NULL;
+
+  g_return_if_fail (G_IS_ASYNC_INITABLE (context));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  ide_async_helper_run (context,
+                        cancellable,
+                        callback,
+                        user_data,
+                        ide_context_init_build_system,
+                        ide_context_init_vcs,
+                        ide_context_init_project_name,
+                        ide_context_init_unsaved_files,
+                        NULL);
+}
+
+static gboolean
+ide_context_init_finish (GAsyncInitable  *initable,
+                         GAsyncResult    *result,
+                         GError         **error)
+{
+  GTask *task = (GTask *)result;
+
+  g_return_val_if_fail (IDE_IS_CONTEXT (initable), FALSE);
+
+  return g_task_propagate_boolean (task, error);
+}
+
+static void
+async_initable_init (GAsyncInitableIface *iface)
+{
+  iface->init_async = ide_context_init_async;
+  iface->init_finish = ide_context_init_finish;
+}
diff --git a/libide/ide-context.h b/libide/ide-context.h
new file mode 100644
index 0000000..e11fd81
--- /dev/null
+++ b/libide/ide-context.h
@@ -0,0 +1,58 @@
+/* ide-context.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_CONTEXT_H
+#define IDE_CONTEXT_H
+
+#include <gio/gio.h>
+
+#include "ide-types.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_CONTEXT (ide_context_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeContext, ide_context, IDE, CONTEXT, GObject)
+
+struct _IdeContext
+{
+  GObject parent_instance;
+};
+
+void            ide_context_new_async        (GFile                *project_file,
+                                              GCancellable         *cancellable,
+                                              GAsyncReadyCallback   callback,
+                                              gpointer              user_data);
+IdeContext     *ide_context_new_finish       (GAsyncResult         *result,
+                                              GError              **error);
+
+IdeBuildSystem   *ide_context_get_build_system   (IdeContext  *context);
+IdeDeviceManager *ide_context_get_device_manager (IdeContext  *context);
+IdeProject       *ide_context_get_project        (IdeContext  *context);
+GFile            *ide_context_get_project_file   (IdeContext  *context);
+gpointer          ide_context_get_service_typed  (IdeContext  *context,
+                                                  GType        service_type);
+IdeUnsavedFiles  *ide_context_get_unsaved_files  (IdeContext  *context);
+IdeVcs           *ide_context_get_vcs            (IdeContext  *context);
+const gchar      *ide_context_get_root_build_dir (IdeContext  *context);
+void              ide_context_set_root_build_dir (IdeContext  *context,
+                                                  const gchar *root_build_dir);
+
+G_END_DECLS
+
+#endif /* IDE_CONTEXT_H */
diff --git a/libide/ide-debugger.h b/libide/ide-debugger.h
new file mode 100644
index 0000000..d12ac06
--- /dev/null
+++ b/libide/ide-debugger.h
@@ -0,0 +1,40 @@
+/* ide-debugger.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_DEBUGGER_H
+#define IDE_DEBUGGER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DEBUGGER               (ide_debugger_get_type ())
+#define IDE_DEBUGGER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_DEBUGGER, IdeDebugger))
+#define IDE_IS_DEBUGGER(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_DEBUGGER))
+#define IDE_DEBUGGER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), IDE_TYPE_DEBUGGER, 
IdeDebuggerInterface))
+
+struct _IdeDebuggerInterface
+{
+  GTypeInterface parent;
+};
+
+GType ide_debugger_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_DEBUGGER_H */
diff --git a/libide/ide-deployer.h b/libide/ide-deployer.h
new file mode 100644
index 0000000..1daece6
--- /dev/null
+++ b/libide/ide-deployer.h
@@ -0,0 +1,37 @@
+/* ide-deployer.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_DEPLOYER_H
+#define IDE_DEPLOYER_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DEPLOYER (ide_deployer_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeDeployer, ide_deployer, IDE, DEPLOYER, IdeObject)
+
+struct _IdeDeployerClass
+{
+  GObjectClass parent;
+};
+
+G_END_DECLS
+
+#endif /* IDE_DEPLOYER_H */
diff --git a/libide/ide-device-manager.c b/libide/ide-device-manager.c
new file mode 100644
index 0000000..518ac0b
--- /dev/null
+++ b/libide/ide-device-manager.c
@@ -0,0 +1,297 @@
+/* ide-device-manager.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-device.h"
+#include "ide-device-manager.h"
+#include "ide-device-provider.h"
+
+#include "local/ide-local-device.h"
+
+typedef struct
+{
+  GPtrArray *devices;
+  GPtrArray *providers;
+} IdeDeviceManagerPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeDeviceManager, ide_device_manager,
+                            IDE_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_SETTLED,
+  LAST_PROP
+};
+
+enum {
+  DEVICE_ADDED,
+  DEVICE_REMOVED,
+  LAST_SIGNAL
+};
+
+static guint gSignals [LAST_SIGNAL];
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+gboolean
+ide_device_manager_get_settled (IdeDeviceManager *self)
+{
+  IdeDeviceManagerPrivate *priv;
+  gsize i;
+
+  g_return_val_if_fail (IDE_IS_DEVICE_MANAGER (self), NULL);
+
+  priv = ide_device_manager_get_instance_private (self);
+
+  for (i = 0; i < priv->providers->len; i++)
+    {
+      IdeDeviceProvider *provider;
+
+      provider = g_ptr_array_index (priv->providers, i);
+      if (!ide_device_provider_get_settled (provider))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+ide_device_manager_device_notify_settled (IdeDeviceManager  *self,
+                                          GParamSpec        *pspec,
+                                          IdeDeviceProvider *provider)
+{
+  g_return_if_fail (IDE_IS_DEVICE_MANAGER (self));
+  g_return_if_fail (IDE_IS_DEVICE_PROVIDER (provider));
+
+  g_object_notify_by_pspec (G_OBJECT (self),
+                            gParamSpecs [PROP_SETTLED]);
+}
+
+static void
+ide_device_manager_device_added (IdeDeviceManager  *self,
+                                 IdeDevice         *device,
+                                 IdeDeviceProvider *provider)
+{
+  IdeDeviceManagerPrivate *priv = ide_device_manager_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_DEVICE_MANAGER (self));
+  g_return_if_fail (IDE_IS_DEVICE (device));
+  g_return_if_fail (IDE_IS_DEVICE_PROVIDER (provider));
+
+  g_ptr_array_add (priv->devices, g_object_ref (device));
+
+  g_signal_emit (self, gSignals [DEVICE_ADDED], 0, provider, device);
+}
+
+static void
+ide_device_manager_device_removed (IdeDeviceManager  *self,
+                                  IdeDevice         *device,
+                                  IdeDeviceProvider *provider)
+{
+  IdeDeviceManagerPrivate *priv = ide_device_manager_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_DEVICE_MANAGER (self));
+  g_return_if_fail (IDE_IS_DEVICE (device));
+  g_return_if_fail (IDE_IS_DEVICE_PROVIDER (provider));
+
+  g_signal_handlers_disconnect_by_func (provider,
+                                        G_CALLBACK (ide_device_manager_device_notify_settled),
+                                        self);
+  g_signal_handlers_disconnect_by_func (provider,
+                                        G_CALLBACK (ide_device_manager_device_added),
+                                        self);
+  g_signal_handlers_disconnect_by_func (provider,
+                                        G_CALLBACK (ide_device_manager_device_removed),
+                                        self);
+
+  if (g_ptr_array_remove (priv->devices, device))
+    g_signal_emit (self, gSignals [DEVICE_REMOVED], 0, provider, device);
+}
+
+void
+ide_device_manager_add_provider (IdeDeviceManager  *self,
+                                 IdeDeviceProvider *provider)
+{
+  IdeDeviceManagerPrivate *priv = ide_device_manager_get_instance_private (self);
+  GPtrArray *devices;
+  guint i;
+
+  g_return_if_fail (IDE_IS_DEVICE_MANAGER (self));
+  g_return_if_fail (IDE_IS_DEVICE_PROVIDER (provider));
+
+  for (i = 0; i < priv->providers->len; i++)
+    {
+      if (provider == g_ptr_array_index (priv->providers, i))
+        {
+          g_warning ("Cannot add provider, already registered.");
+          return;
+        }
+    }
+
+  g_ptr_array_add (priv->providers, g_object_ref (provider));
+
+  g_signal_connect_object (provider,
+                           "notify::settled",
+                           G_CALLBACK (ide_device_manager_device_notify_settled),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (provider,
+                           "device-added",
+                           G_CALLBACK (ide_device_manager_device_added),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (provider,
+                           "device-removed",
+                           G_CALLBACK (ide_device_manager_device_removed),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  devices = ide_device_provider_get_devices (provider);
+
+  for (i = 0; i < devices->len; i++)
+    {
+      IdeDevice *device;
+
+      device = g_ptr_array_index (devices, i);
+      ide_device_manager_device_added (self, device, provider);
+    }
+}
+
+/**
+ * ide_device_manager_get_devices:
+ *
+ * Retrieves all of the devices that are registered with the #IdeDeviceManager.
+ *
+ * Returns: (transfer container) (element-type IdeDevice*): An array of devices
+ *   registered with the #IdeManager.
+ */
+GPtrArray *
+ide_device_manager_get_devices (IdeDeviceManager *self)
+{
+  IdeDeviceManagerPrivate *priv;
+  GPtrArray *ret;
+  guint i;
+
+  g_return_val_if_fail (IDE_IS_DEVICE_MANAGER (self), NULL);
+
+  priv = ide_device_manager_get_instance_private (self);
+
+  ret = g_ptr_array_new_with_free_func (g_object_unref);
+
+  for (i = 0; i < priv->devices->len; i++)
+    {
+      IdeDevice *device;
+
+      device = g_ptr_array_index (priv->devices, i);
+      g_ptr_array_add (ret, g_object_ref (device));
+    }
+
+  return ret;
+}
+
+static void
+ide_device_manager_add_local (IdeDeviceManager *manager)
+{
+  IdeDeviceManagerPrivate *priv;
+  IdeContext *context;
+  IdeDevice *device;
+
+  g_return_if_fail (IDE_IS_DEVICE_MANAGER (manager));
+
+  priv = ide_device_manager_get_instance_private (manager);
+
+  context = ide_object_get_context (IDE_OBJECT (manager));
+  device = g_object_new (IDE_TYPE_LOCAL_DEVICE,
+                         "context", context,
+                         NULL);
+  g_ptr_array_add (priv->devices, g_object_ref (device));
+  g_clear_object (&device);
+}
+
+static void
+ide_device_manager_constructed (GObject *object)
+{
+  IdeDeviceManager *self = (IdeDeviceManager *)object;
+
+  g_return_if_fail (IDE_IS_DEVICE_MANAGER (self));
+
+  G_OBJECT_CLASS (ide_device_manager_parent_class)->constructed (object);
+
+  ide_device_manager_add_local (self);
+}
+
+static void
+ide_device_manager_finalize (GObject *object)
+{
+  IdeDeviceManager *self = (IdeDeviceManager *)object;
+  IdeDeviceManagerPrivate *priv = ide_device_manager_get_instance_private (self);
+
+  g_clear_pointer (&priv->devices, g_ptr_array_unref);
+  g_clear_pointer (&priv->providers, g_ptr_array_unref);
+
+  G_OBJECT_CLASS (ide_device_manager_parent_class)->finalize (object);
+}
+
+static void
+ide_device_manager_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  IdeDeviceManager *self = IDE_DEVICE_MANAGER(object);
+
+  switch (prop_id)
+    {
+    case PROP_SETTLED:
+      g_value_set_boolean (value, ide_device_manager_get_settled (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    }
+}
+
+static void
+ide_device_manager_class_init (IdeDeviceManagerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed = ide_device_manager_constructed;
+  object_class->finalize = ide_device_manager_finalize;
+  object_class->get_property = ide_device_manager_get_property;
+
+  gParamSpecs [PROP_SETTLED] =
+    g_param_spec_boolean ("settled",
+                          _("Settled"),
+                          _("If the device providers have settled."),
+                          FALSE,
+                          (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_SETTLED,
+                                   gParamSpecs [PROP_SETTLED]);
+}
+
+static void
+ide_device_manager_init (IdeDeviceManager *self)
+{
+  IdeDeviceManagerPrivate *priv = ide_device_manager_get_instance_private (self);
+
+  priv->devices = g_ptr_array_new_with_free_func (g_object_unref);
+  priv->providers = g_ptr_array_new_with_free_func (g_object_unref);
+}
diff --git a/libide/ide-device-manager.h b/libide/ide-device-manager.h
new file mode 100644
index 0000000..d420b16
--- /dev/null
+++ b/libide/ide-device-manager.h
@@ -0,0 +1,46 @@
+/* ide-device-manager.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_DEVICE_MANAGER_H
+#define IDE_DEVICE_MANAGER_H
+
+#include "ide-object.h"
+#include "ide-device-manager.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DEVICE_MANAGER (ide_device_manager_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeDeviceManager, ide_device_manager,
+                      IDE, DEVICE_MANAGER, IdeObject)
+
+struct _IdeDeviceManager
+{
+  IdeObject parent_instance;
+};
+
+void       ide_device_manager_add_provider    (IdeDeviceManager  *self,
+                                               IdeDeviceProvider *provider);
+GPtrArray *ide_device_manager_get_devices     (IdeDeviceManager  *self);
+gboolean   ide_device_manager_get_settled     (IdeDeviceManager  *self);
+void       ide_device_manager_remove_provider (IdeDeviceManager  *self,
+                                               IdeDeviceProvider *provider);
+
+G_END_DECLS
+
+#endif /* IDE_DEVICE_MANAGER_H */
diff --git a/libide/ide-device-provider.c b/libide/ide-device-provider.c
new file mode 100644
index 0000000..b3f20bc
--- /dev/null
+++ b/libide/ide-device-provider.c
@@ -0,0 +1,187 @@
+/* ide-device-provider.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-device-provider.h"
+
+typedef struct
+{
+  GPtrArray *devices;
+} IdeDeviceProviderPrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (IdeDeviceProvider, ide_device_provider, IDE_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_SETTLED,
+  LAST_PROP
+};
+
+enum {
+  DEVICE_ADDED,
+  DEVICE_REMOVED,
+  LAST_SIGNAL
+};
+
+static guint gSignals [LAST_SIGNAL];
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+gboolean
+ide_device_provider_get_settled (IdeDeviceProvider *provider)
+{
+  g_return_val_if_fail (IDE_IS_DEVICE_PROVIDER (provider), FALSE);
+
+  if (IDE_DEVICE_PROVIDER_GET_CLASS (provider)->get_settled)
+    return IDE_DEVICE_PROVIDER_GET_CLASS (provider)->get_settled (provider);
+
+  return TRUE;
+}
+
+GPtrArray *
+ide_device_provider_get_devices (IdeDeviceProvider *provider)
+{
+  IdeDeviceProviderPrivate *priv = ide_device_provider_get_instance_private (provider);
+
+  g_return_val_if_fail (IDE_IS_DEVICE_PROVIDER (provider), NULL);
+
+  return priv->devices;
+}
+
+static void
+ide_device_provider_real_device_added (IdeDeviceProvider *provider,
+                                       IdeDevice         *device)
+{
+  IdeDeviceProviderPrivate *priv = ide_device_provider_get_instance_private (provider);
+
+  g_ptr_array_add (priv->devices, g_object_ref (device));
+}
+
+static void
+ide_device_provider_real_device_removed (IdeDeviceProvider *provider,
+                                         IdeDevice         *device)
+{
+  IdeDeviceProviderPrivate *priv = ide_device_provider_get_instance_private (provider);
+
+  g_ptr_array_remove (priv->devices, device);
+}
+
+void
+ide_device_provider_device_added (IdeDeviceProvider *provider,
+                                  IdeDevice         *device)
+{
+  g_return_if_fail (IDE_IS_DEVICE_PROVIDER (provider));
+  g_return_if_fail (IDE_IS_DEVICE (device));
+
+  g_signal_emit (provider, gSignals [DEVICE_ADDED], 0, device);
+}
+
+void
+ide_device_provider_device_removed (IdeDeviceProvider *provider,
+                                    IdeDevice         *device)
+{
+  g_return_if_fail (IDE_IS_DEVICE_PROVIDER (provider));
+  g_return_if_fail (IDE_IS_DEVICE (device));
+
+  g_signal_emit (provider, gSignals [DEVICE_REMOVED], 0, device);
+}
+
+static void
+ide_device_provider_finalize (GObject *object)
+{
+  IdeDeviceProvider *self = (IdeDeviceProvider *)object;
+  IdeDeviceProviderPrivate *priv = ide_device_provider_get_instance_private (self);
+
+  g_clear_pointer (&priv->devices, g_ptr_array_unref);
+
+  G_OBJECT_CLASS (ide_device_provider_parent_class)->finalize (object);
+}
+
+static void
+ide_device_provider_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  IdeDeviceProvider *self = IDE_DEVICE_PROVIDER (object);
+
+  switch (prop_id)
+    {
+    case PROP_SETTLED:
+      g_value_set_boolean (value, ide_device_provider_get_settled (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    }
+}
+
+static void
+ide_device_provider_class_init (IdeDeviceProviderClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_device_provider_finalize;
+  object_class->get_property = ide_device_provider_get_property;
+
+  klass->device_added = ide_device_provider_real_device_added;
+  klass->device_removed = ide_device_provider_real_device_removed;
+
+  gParamSpecs [PROP_SETTLED] =
+    g_param_spec_boolean ("settled",
+                          _("Settled"),
+                          _("If device probing has settled."),
+                          FALSE,
+                          (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_SETTLED,
+                                   gParamSpecs [PROP_SETTLED]);
+
+  gSignals [DEVICE_ADDED] =
+    g_signal_new ("device-added",
+                  IDE_TYPE_DEVICE_PROVIDER,
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (IdeDeviceProviderClass, device_added),
+                  NULL,
+                  NULL,
+                  g_cclosure_marshal_generic,
+                  G_TYPE_NONE,
+                  1,
+                  IDE_TYPE_DEVICE);
+
+  gSignals [DEVICE_REMOVED] =
+    g_signal_new ("device-removed",
+                  IDE_TYPE_DEVICE_PROVIDER,
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (IdeDeviceProviderClass, device_removed),
+                  NULL,
+                  NULL,
+                  g_cclosure_marshal_generic,
+                  G_TYPE_NONE,
+                  1,
+                  IDE_TYPE_DEVICE);
+}
+
+static void
+ide_device_provider_init (IdeDeviceProvider *self)
+{
+  IdeDeviceProviderPrivate *priv;
+
+  priv = ide_device_provider_get_instance_private (self);
+
+  priv->devices = g_ptr_array_new_with_free_func (g_object_unref);
+}
diff --git a/libide/ide-device-provider.h b/libide/ide-device-provider.h
new file mode 100644
index 0000000..8496159
--- /dev/null
+++ b/libide/ide-device-provider.h
@@ -0,0 +1,53 @@
+/* ide-device-provider.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_DEVICE_PROVIDER_H
+#define IDE_DEVICE_PROVIDER_H
+
+#include "ide-device.h"
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DEVICE_PROVIDER            (ide_device_provider_get_type())
+#define IDE_DEVICE_PROVIDER_EXTENSION_POINT "org.gnome.libide.extensions.device-provider"
+
+G_DECLARE_DERIVABLE_TYPE (IdeDeviceProvider, ide_device_provider,
+                          IDE, DEVICE_PROVIDER, IdeObject)
+
+struct _IdeDeviceProviderClass
+{
+  GObjectClass parent;
+
+  void     (*device_added)   (IdeDeviceProvider *provider,
+                              IdeDevice         *device);
+  void     (*device_removed) (IdeDeviceProvider *provider,
+                              IdeDevice         *device);
+  gboolean (*get_settled)    (IdeDeviceProvider *provider);
+};
+
+void       ide_device_provider_device_added   (IdeDeviceProvider *provider,
+                                               IdeDevice         *device);
+void       ide_device_provider_device_removed (IdeDeviceProvider *provider,
+                                               IdeDevice         *device);
+GPtrArray *ide_device_provider_get_devices    (IdeDeviceProvider *provider);
+gboolean   ide_device_provider_get_settled    (IdeDeviceProvider *provider);
+
+G_END_DECLS
+
+#endif /* IDE_DEVICE_PROVIDER_H */
diff --git a/libide/ide-device.c b/libide/ide-device.c
new file mode 100644
index 0000000..b2af99f
--- /dev/null
+++ b/libide/ide-device.c
@@ -0,0 +1,263 @@
+/* ide-device.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-device.h"
+
+typedef struct
+{
+  gchar *display_name;
+  gchar *id;
+} IdeDevicePrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (IdeDevice, ide_device, IDE_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_DISPLAY_NAME,
+  PROP_ID,
+  PROP_SYSTEM_TYPE,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+/**
+ * ide_device_get_config:
+ * @device: A #IdeDevice.
+ *
+ * Retrieves any custom configuration that is required to build for the
+ * device. Such values might include additional options to autoconf
+ * or paths to cross-compilers.
+ *
+ * Returns: (transfer none) (nullable): A #GKeyFile or %NULL.
+ */
+GKeyFile *
+ide_device_get_config (IdeDevice *device)
+{
+  g_return_val_if_fail (IDE_IS_DEVICE (device), NULL);
+
+  if (IDE_DEVICE_GET_CLASS (device)->get_config)
+    return IDE_DEVICE_GET_CLASS (device)->get_config (device);
+
+  return NULL;
+}
+
+/**
+ * ide_device_get_display_name:
+ *
+ * This function returns the name of the device. If no name has been set, then
+ * %NULL is returned.
+ *
+ * In some cases, this value wont be available until additional information
+ * has been probed from the device.
+ *
+ * Returns: (nullable): A string containing the display name for the device.
+ */
+const gchar *
+ide_device_get_display_name (IdeDevice *device)
+{
+  IdeDevicePrivate *priv = ide_device_get_instance_private (device);
+
+  g_return_val_if_fail (IDE_IS_DEVICE (device), NULL);
+
+  return priv->display_name;
+}
+
+void
+ide_device_set_display_name (IdeDevice   *device,
+                             const gchar *display_name)
+{
+  IdeDevicePrivate *priv = ide_device_get_instance_private (device);
+
+  g_return_if_fail (IDE_IS_DEVICE (device));
+
+  if (display_name != priv->display_name)
+    {
+      g_free (priv->display_name);
+      priv->display_name = g_strdup (display_name);
+      g_object_notify_by_pspec (G_OBJECT (device),
+                                gParamSpecs [PROP_DISPLAY_NAME]);
+    }
+}
+
+/**
+ * ide_device_get_id:
+ *
+ * Retrieves the "id" property of the #IdeDevice. This is generally not a
+ * user friendly name as it is often a guid.
+ *
+ * Returns: A unique identifier for the device.
+ */
+const gchar *
+ide_device_get_id (IdeDevice *device)
+{
+  IdeDevicePrivate *priv = ide_device_get_instance_private (device);
+
+  g_return_val_if_fail (IDE_IS_DEVICE (device), NULL);
+
+  return priv->id;
+}
+
+void
+ide_device_set_id (IdeDevice   *device,
+                   const gchar *id)
+{
+  IdeDevicePrivate *priv = ide_device_get_instance_private (device);
+
+  g_return_if_fail (IDE_IS_DEVICE (device));
+
+  if (id != priv->id)
+    {
+      g_free (priv->id);
+      priv->id = g_strdup (id);
+      g_object_notify_by_pspec (G_OBJECT (device), gParamSpecs [PROP_ID]);
+    }
+}
+
+/**
+ * ide_device_get_system_type:
+ *
+ * This is the description of the system we are building for. Commonly, this
+ * is referred to as a "system_type". A combination of the machine architecture
+ * such as x86_64, the operating system, and the libc.
+ *
+ * "x86_64-linux-gnu" might be one such system.
+ *
+ * Returns: A string containing the system type.
+ */
+const gchar *
+ide_device_get_system_type (IdeDevice *device)
+{
+  IdeDeviceClass *klass;
+  const gchar *ret = NULL;
+
+  g_return_val_if_fail (IDE_IS_DEVICE (device), NULL);
+
+  klass = IDE_DEVICE_GET_CLASS (device);
+
+  if (klass->get_system_type)
+    ret = klass->get_system_type (device);
+
+  return ret;
+}
+
+static void
+ide_device_finalize (GObject *object)
+{
+  IdeDevice *self = (IdeDevice *)object;
+  IdeDevicePrivate *priv = ide_device_get_instance_private (self);
+
+  g_clear_pointer (&priv->display_name, g_free);
+  g_clear_pointer (&priv->id, g_free);
+
+  G_OBJECT_CLASS (ide_device_parent_class)->finalize (object);
+}
+
+static void
+ide_device_get_property (GObject    *object,
+                         guint       prop_id,
+                         GValue     *value,
+                         GParamSpec *pspec)
+{
+  IdeDevice *self = IDE_DEVICE (object);
+
+  switch (prop_id)
+    {
+    case PROP_DISPLAY_NAME:
+      g_value_set_string (value, ide_device_get_display_name (self));
+      break;
+
+    case PROP_ID:
+      g_value_set_string (value, ide_device_get_id (self));
+      break;
+
+    case PROP_SYSTEM_TYPE:
+      g_value_set_string (value, ide_device_get_system_type (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_device_set_property (GObject      *object,
+                         guint         prop_id,
+                         const GValue *value,
+                         GParamSpec   *pspec)
+{
+  IdeDevice *self = IDE_DEVICE (object);
+
+  switch (prop_id)
+    {
+    case PROP_DISPLAY_NAME:
+      ide_device_set_display_name (self, g_value_get_string (value));
+      break;
+
+    case PROP_ID:
+      ide_device_set_id (self, ide_device_get_id (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_device_class_init (IdeDeviceClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_device_finalize;
+  object_class->get_property = ide_device_get_property;
+  object_class->set_property = ide_device_set_property;
+
+  gParamSpecs [PROP_DISPLAY_NAME] =
+    g_param_spec_string ("display-name",
+                         _("Display Name"),
+                         _("The display name of the device."),
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_DISPLAY_NAME,
+                                   gParamSpecs [PROP_DISPLAY_NAME]);
+
+  gParamSpecs [PROP_ID] =
+    g_param_spec_string ("id",
+                         _("Id"),
+                         _("The device identifier."),
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_ID,
+                                   gParamSpecs [PROP_ID]);
+
+  gParamSpecs [PROP_SYSTEM_TYPE] =
+    g_param_spec_string ("system-type",
+                         _("System Type"),
+                         _("The system type for which to compile."),
+                         NULL,
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_SYSTEM_TYPE,
+                                   gParamSpecs [PROP_SYSTEM_TYPE]);
+}
+
+static void
+ide_device_init (IdeDevice *self)
+{
+}
diff --git a/libide/ide-device.h b/libide/ide-device.h
new file mode 100644
index 0000000..2d37988
--- /dev/null
+++ b/libide/ide-device.h
@@ -0,0 +1,49 @@
+/* ide-device.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_DEVICE_H
+#define IDE_DEVICE_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DEVICE (ide_device_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeDevice, ide_device, IDE, DEVICE, IdeObject)
+
+struct _IdeDeviceClass
+{
+  GObjectClass parent;
+
+  GKeyFile    *(*get_config)      (IdeDevice *device);
+  const gchar *(*get_system_type) (IdeDevice *device);
+};
+
+GKeyFile    *ide_device_get_config       (IdeDevice   *device);
+const gchar *ide_device_get_display_name (IdeDevice   *device);
+void         ide_device_set_display_name (IdeDevice   *device,
+                                          const gchar *display_name);
+const gchar *ide_device_get_id           (IdeDevice   *device);
+void         ide_device_set_id           (IdeDevice   *device,
+                                          const gchar *id);
+const gchar *ide_device_get_system_type  (IdeDevice   *device);
+
+G_END_DECLS
+
+#endif /* IDE_DEVICE_H */
diff --git a/libide/ide-diagnostic-provider.h b/libide/ide-diagnostic-provider.h
new file mode 100644
index 0000000..2f31e43
--- /dev/null
+++ b/libide/ide-diagnostic-provider.h
@@ -0,0 +1,40 @@
+/* ide-diagnostics.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_DIAGNOSTIC_PROVIDER_H
+#define IDE_DIAGNOSTIC_PROVIDER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DIAGNOSTIC_PROVIDER               (ide_diagnostic_provider_get_type ())
+#define IDE_DIAGNOSTIC_PROVIDER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
IDE_TYPE_DIAGNOSTIC_PROVIDER, IdeDiagnosticProvider))
+#define IDE_IS_DIAGNOSTIC_PROVIDER(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
IDE_TYPE_DIAGNOSTIC_PROVIDER))
+#define IDE_DIAGNOSTIC_PROVIDER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), 
IDE_TYPE_DIAGNOSTIC_PROVIDER, IdeDiagnosticProviderInterface))
+
+struct _IdeDiagnosticProviderInterface
+{
+  GTypeInterface parent;
+};
+
+GType ide_diagnostic_provider_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_DIAGNOSTIC_PROVIDER_H */
diff --git a/libide/ide-diagnostic.h b/libide/ide-diagnostic.h
new file mode 100644
index 0000000..f077f45
--- /dev/null
+++ b/libide/ide-diagnostic.h
@@ -0,0 +1,40 @@
+/* ide-diagnostic.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_DIAGNOSTIC_H
+#define IDE_DIAGNOSTIC_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_DIAGNOSTIC               (ide_diagnostic_get_type ())
+#define IDE_DIAGNOSTIC(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_DIAGNOSTIC, 
IdeDiagnostic))
+#define IDE_IS_DIAGNOSTIC(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_DIAGNOSTIC))
+#define IDE_DIAGNOSTIC_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), IDE_TYPE_DIAGNOSTIC, 
IdeDiagnosticInterface))
+
+struct _IdeDiagnosticInterface
+{
+  GTypeInterface parent;
+};
+
+GType ide_diagnostic_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_DIAGNOSTIC_H */
diff --git a/libide/ide-executable.h b/libide/ide-executable.h
new file mode 100644
index 0000000..1e80494
--- /dev/null
+++ b/libide/ide-executable.h
@@ -0,0 +1,40 @@
+/* ide-executable.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_EXECUTABLE_H
+#define IDE_EXECUTABLE_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_EXECUTABLE               (ide_executable_get_type ())
+#define IDE_EXECUTABLE(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_EXECUTABLE, 
IdeExecutable))
+#define IDE_IS_EXECUTABLE(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_EXECUTABLE))
+#define IDE_EXECUTABLE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), IDE_TYPE_EXECUTABLE, 
IdeExecutableInterface))
+
+struct _IdeExecutableInterface
+{
+  GTypeInterface parent;
+};
+
+GType ide_executable_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_EXECUTABLE_H */
diff --git a/libide/ide-executer.h b/libide/ide-executer.h
new file mode 100644
index 0000000..89550e3
--- /dev/null
+++ b/libide/ide-executer.h
@@ -0,0 +1,40 @@
+/* ide-executer.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_EXECUTER_H
+#define IDE_EXECUTER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_EXECUTER               (ide_executer_get_type ())
+#define IDE_EXECUTER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_EXECUTER, IdeExecuter))
+#define IDE_IS_EXECUTER(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_EXECUTER))
+#define IDE_EXECUTER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), IDE_TYPE_EXECUTER, 
IdeExecuterInterface))
+
+struct _IdeExecuterInterface
+{
+  GTypeInterface parent;
+};
+
+GType ide_executer_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_EXECUTER_H */
diff --git a/libide/ide-file.c b/libide/ide-file.c
new file mode 100644
index 0000000..cedcccd
--- /dev/null
+++ b/libide/ide-file.c
@@ -0,0 +1,138 @@
+/* ide-file.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-file.h"
+
+typedef struct
+{
+  GFile *file;
+} IdeFilePrivate;
+
+enum
+{
+  PROP_0,
+  PROP_FILE,
+  LAST_PROP
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeFile, ide_file, IDE_TYPE_OBJECT)
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+GFile *
+ide_file_get_file (IdeFile *self)
+{
+  IdeFilePrivate *priv = ide_file_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_FILE (self), NULL);
+
+  return priv->file;
+}
+
+static void
+ide_file_set_file (IdeFile *self,
+                   GFile   *file)
+{
+  IdeFilePrivate *priv = ide_file_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_FILE (self));
+  g_return_if_fail (G_IS_FILE (file));
+
+  if (file != priv->file)
+    {
+      if (g_set_object (&priv->file, file))
+        g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_FILE]);
+    }
+}
+
+static void
+ide_file_finalize (GObject *object)
+{
+  IdeFile *self = (IdeFile *)object;
+  IdeFilePrivate *priv = ide_file_get_instance_private (self);
+
+  g_clear_object (&priv->file);
+
+  G_OBJECT_CLASS (ide_file_parent_class)->finalize (object);
+}
+
+static void
+ide_file_get_property (GObject    *object,
+                       guint       prop_id,
+                       GValue     *value,
+                       GParamSpec *pspec)
+{
+  IdeFile *self = (IdeFile *)object;
+
+  switch (prop_id)
+    {
+    case PROP_FILE:
+      g_value_set_object (value, ide_file_get_file (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_file_set_property (GObject      *object,
+                       guint         prop_id,
+                       const GValue *value,
+                       GParamSpec   *pspec)
+{
+  IdeFile *self = (IdeFile *)object;
+
+  switch (prop_id)
+    {
+    case PROP_FILE:
+      ide_file_set_file (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_file_class_init (IdeFileClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_file_finalize;
+  object_class->get_property = ide_file_get_property;
+  object_class->set_property = ide_file_set_property;
+
+  gParamSpecs [PROP_FILE] =
+    g_param_spec_object ("file",
+                         _("File"),
+                         _("The path to the underlying file."),
+                         G_TYPE_FILE,
+                         (G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_FILE,
+                                   gParamSpecs [PROP_FILE]);
+}
+
+static void
+ide_file_init (IdeFile *file)
+{
+}
diff --git a/libide/ide-file.h b/libide/ide-file.h
new file mode 100644
index 0000000..7b2931c
--- /dev/null
+++ b/libide/ide-file.h
@@ -0,0 +1,37 @@
+/* ide-file.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_FILE_H
+#define IDE_FILE_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_FILE (ide_file_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeFile, ide_file, IDE, FILE, IdeObject)
+
+struct _IdeFile
+{
+  IdeObject parent_instance;
+};
+
+G_END_DECLS
+
+#endif /* IDE_FILE_H */
diff --git a/libide/ide-global.h b/libide/ide-global.h
new file mode 100644
index 0000000..6531ca8
--- /dev/null
+++ b/libide/ide-global.h
@@ -0,0 +1,31 @@
+/* ide-global.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_GLOBAL_H
+#define IDE_GLOBAL_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+const gchar *ide_get_program_name (void);
+void         ide_set_program_name (const gchar *program_name);
+
+G_END_DECLS
+
+#endif /* IDE_GLOBAL_H */
diff --git a/libide/ide-indenter.h b/libide/ide-indenter.h
new file mode 100644
index 0000000..5fb5ffb
--- /dev/null
+++ b/libide/ide-indenter.h
@@ -0,0 +1,40 @@
+/* ide-indenter.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_INDENTER_H
+#define IDE_INDENTER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_INDENTER               (ide_indenter_get_type ())
+#define IDE_INDENTER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_INDENTER, IdeIndenter))
+#define IDE_IS_INDENTER(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_INDENTER))
+#define IDE_INDENTER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), IDE_TYPE_INDENTER, 
IdeIndenterInterface))
+
+struct _IdeIndenterInterface
+{
+  GTypeInterface parent;
+};
+
+GType ide_indenter_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_INDENTER_H */
diff --git a/libide/ide-language.h b/libide/ide-language.h
new file mode 100644
index 0000000..a5d8042
--- /dev/null
+++ b/libide/ide-language.h
@@ -0,0 +1,40 @@
+/* ide-language.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_LANGUAGE_H
+#define IDE_LANGUAGE_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_LANGUAGE               (ide_language_get_type ())
+#define IDE_LANGUAGE(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_LANGUAGE, IdeLanguage))
+#define IDE_IS_LANGUAGE(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_LANGUAGE))
+#define IDE_LANGUAGE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), IDE_TYPE_LANGUAGE, 
IdeLanguageInterface))
+
+struct _IdeLanguageInterface
+{
+  GTypeInterface parent;
+};
+
+GType ide_language_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_LANGUAGE_H */
diff --git a/libide/ide-object.c b/libide/ide-object.c
new file mode 100644
index 0000000..149d1e1
--- /dev/null
+++ b/libide/ide-object.c
@@ -0,0 +1,311 @@
+/* ide-object.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-context.h"
+#include "ide-object.h"
+
+typedef struct
+{
+  IdeContext *context;
+} IdeObjectPrivate;
+
+typedef struct
+{
+  GTask *task;
+  GList *objects;
+  GList *iter;
+  int    io_priority;
+} InitAsyncState;
+
+static void ide_object_new_async_try_next (InitAsyncState *state);
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeObject, ide_object, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_CONTEXT,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+/**
+ * ide_object_get_context:
+ *
+ * Fetches the #IdeObject:context property.
+ *
+ * Returns: (transfer none): An #IdeContext.
+ */
+IdeContext *
+ide_object_get_context (IdeObject *object)
+{
+  IdeObjectPrivate *priv = ide_object_get_instance_private (object);
+
+  g_return_val_if_fail (IDE_IS_OBJECT (object), NULL);
+
+  return priv->context;
+}
+
+static void
+ide_object_set_context (IdeObject  *object,
+                        IdeContext *context)
+{
+  IdeObjectPrivate *priv = ide_object_get_instance_private (object);
+
+  g_return_if_fail (IDE_IS_OBJECT (object));
+  g_return_if_fail (IDE_IS_CONTEXT (context));
+
+  if (context != priv->context)
+    {
+      if (priv->context)
+        {
+          g_object_remove_weak_pointer (G_OBJECT (priv->context),
+                                        (gpointer *)&priv->context);
+          priv->context = NULL;
+        }
+
+      if (context)
+        {
+          priv->context = context;
+          g_object_add_weak_pointer (G_OBJECT (priv->context),
+                                     (gpointer *)&priv->context);
+        }
+    }
+}
+
+static void
+ide_object_finalize (GObject *object)
+{
+  IdeObject *self = (IdeObject *)object;
+  IdeObjectPrivate *priv = ide_object_get_instance_private (self);
+
+  if (priv->context)
+    {
+      g_object_remove_weak_pointer (G_OBJECT (priv->context),
+                                    (gpointer *)&priv->context);
+      priv->context = NULL;
+    }
+
+  G_OBJECT_CLASS (ide_object_parent_class)->finalize (object);
+}
+
+static void
+ide_object_get_property (GObject    *object,
+                         guint       prop_id,
+                         GValue     *value,
+                         GParamSpec *pspec)
+{
+  IdeObject *self = IDE_OBJECT (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONTEXT:
+      g_value_set_object (value, ide_object_get_context (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_object_set_property (GObject      *object,
+                         guint         prop_id,
+                         const GValue *value,
+                         GParamSpec   *pspec)
+{
+  IdeObject *self = IDE_OBJECT (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONTEXT:
+      ide_object_set_context (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_object_class_init (IdeObjectClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_object_finalize;
+  object_class->get_property = ide_object_get_property;
+  object_class->set_property = ide_object_set_property;
+
+  gParamSpecs [PROP_CONTEXT] =
+    g_param_spec_object ("context",
+                         _("Context"),
+                         _("The context that owns the object."),
+                         IDE_TYPE_CONTEXT,
+                         (G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_CONTEXT,
+                                   gParamSpecs [PROP_CONTEXT]);
+}
+
+static void
+ide_object_init (IdeObject *self)
+{
+}
+
+static void
+init_async_state_free (gpointer data)
+{
+  InitAsyncState *state = data;
+
+  if (state)
+    {
+      g_list_foreach (state->objects, (GFunc)g_object_unref, NULL);
+      g_list_free (state->objects);
+      g_slice_free (InitAsyncState, state);
+    }
+}
+
+static void
+ide_object_init_async_cb (GObject      *source_object,
+                          GAsyncResult *result,
+                          gpointer      user_data)
+{
+  InitAsyncState *state = user_data;
+  IdeObject *object = (IdeObject *)source_object;
+  GError *error = NULL;
+
+  g_return_if_fail (!object || IDE_IS_OBJECT (object));
+  g_return_if_fail (state);
+
+  if (!g_async_initable_init_finish (G_ASYNC_INITABLE (object), result, &error))
+    {
+      ide_object_new_async_try_next (state);
+      return;
+    }
+
+  g_task_return_pointer (state->task, g_object_ref (object), g_object_unref);
+  g_object_unref (state->task);
+}
+
+static void
+ide_object_new_async_try_next (InitAsyncState *state)
+{
+  IdeObject *object;
+
+  g_return_if_fail (state);
+
+  if (!state->iter)
+    {
+      g_task_return_new_error (state->task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_NOT_SUPPORTED,
+                               _("No implementations of extension point."));
+      g_object_unref (state->task);
+      return;
+    }
+
+  object = state->iter->data;
+  state->iter = state->iter->next;
+
+  g_async_initable_init_async (G_ASYNC_INITABLE (object),
+                               state->io_priority,
+                               g_task_get_cancellable (state->task),
+                               ide_object_init_async_cb,
+                               state);
+}
+
+void
+ide_object_new_async (const gchar          *extension_point,
+                      int                   io_priority,
+                      GCancellable         *cancellable,
+                      GAsyncReadyCallback   callback,
+                      gpointer              user_data,
+                      const gchar          *first_property,
+                      ...)
+{
+  GIOExtensionPoint *point;
+  InitAsyncState *state;
+  const GList *extensions;
+  const GList *iter;
+  va_list args;
+
+  g_return_if_fail (extension_point);
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  point = g_io_extension_point_lookup (extension_point);
+
+  if (!point)
+    {
+      g_task_report_new_error (NULL, callback, user_data, ide_object_new_async,
+                               G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                               _("No such extension point."));
+      return;
+    }
+
+  extensions = g_io_extension_point_get_extensions (point);
+
+  if (!extensions)
+    {
+      g_task_report_new_error (NULL, callback, user_data, ide_object_new_async,
+                               G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                               _("No implementations of extension point."));
+      return;
+    }
+
+  state = g_slice_new0 (InitAsyncState);
+  state->io_priority = io_priority;
+  state->task = g_task_new (NULL, cancellable, callback, user_data);
+  g_task_set_task_data (state->task, state, init_async_state_free);
+
+  for (iter = extensions; iter; iter = iter->next)
+    {
+      GIOExtension *extension = iter->data;
+      GObject *object;
+      GType type_id;
+
+      type_id = g_io_extension_get_type (extension);
+
+      if (!g_type_is_a (type_id, G_TYPE_ASYNC_INITABLE))
+        continue;
+
+      va_start (args, first_property);
+      object = g_object_new_valist (type_id, first_property, args);
+      va_end (args);
+
+      state->objects = g_list_append (state->objects, object);
+
+      if (!state->iter)
+        state->iter = state->objects;
+    }
+
+  ide_object_new_async_try_next (state);
+}
+
+IdeObject *
+ide_object_new_finish  (GAsyncResult  *result,
+                        GError       **error)
+{
+  GTask *task = (GTask *)result;
+
+  g_return_val_if_fail (G_IS_TASK (task), NULL);
+
+  return g_task_propagate_pointer (task, error);
+}
diff --git a/libide/ide-object.h b/libide/ide-object.h
new file mode 100644
index 0000000..db3cbd8
--- /dev/null
+++ b/libide/ide-object.h
@@ -0,0 +1,50 @@
+/* ide-object.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_OBJECT_H
+#define IDE_OBJECT_H
+
+#include <gio/gio.h>
+
+#include "ide-types.h"
+
+G_BEGIN_DECLS
+
+G_DECLARE_DERIVABLE_TYPE (IdeObject, ide_object, IDE, OBJECT, GObject)
+
+#define IDE_TYPE_OBJECT (ide_object_get_type())
+
+struct _IdeObjectClass
+{
+  GObjectClass parent;
+};
+
+IdeContext *ide_object_get_context (IdeObject            *object);
+void        ide_object_new_async   (const gchar          *extension_point,
+                                    int                   io_priority,
+                                    GCancellable         *cancellable,
+                                    GAsyncReadyCallback   callback,
+                                    gpointer              user_data,
+                                    const gchar          *first_property,
+                                    ...);
+IdeObject  *ide_object_new_finish  (GAsyncResult         *result,
+                                    GError              **error);
+
+G_END_DECLS
+
+#endif /* IDE_OBJECT_H */
diff --git a/libide/ide-process.h b/libide/ide-process.h
new file mode 100644
index 0000000..d6ae844
--- /dev/null
+++ b/libide/ide-process.h
@@ -0,0 +1,40 @@
+/* ide-process.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_PROCESS_H
+#define IDE_PROCESS_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_PROCESS               (ide_process_get_type ())
+#define IDE_PROCESS(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_PROCESS, IdeProcess))
+#define IDE_IS_PROCESS(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_PROCESS))
+#define IDE_PROCESS_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), IDE_TYPE_PROCESS, 
IdeProcessInterface))
+
+struct _IdeProcessInterface
+{
+  GTypeInterface parent;
+};
+
+GType ide_process_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_PROCESS_H */
diff --git a/libide/ide-project-file.c b/libide/ide-project-file.c
new file mode 100644
index 0000000..bb96d10
--- /dev/null
+++ b/libide/ide-project-file.c
@@ -0,0 +1,145 @@
+/* ide-project-file.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-project-file.h"
+
+typedef struct
+{
+  GFileInfo *file_info;
+} IdeProjectFilePrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeProjectFile, ide_project_file,
+                            IDE_TYPE_PROJECT_ITEM)
+
+enum {
+  PROP_0,
+  PROP_FILE_INFO,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+IdeProjectFile *
+ide_project_file_new (IdeProjectItem *parent,
+                      GFileInfo      *file_info)
+{
+  return g_object_new (IDE_TYPE_PROJECT_FILE,
+                       "parent", parent,
+                       "file-info", file_info,
+                       NULL);
+}
+
+GFileInfo *
+ide_project_file_get_file_info (IdeProjectFile *file)
+{
+  IdeProjectFilePrivate *priv = ide_project_file_get_instance_private (file);
+
+  g_return_val_if_fail (IDE_IS_PROJECT_FILE (file), NULL);
+
+  return priv->file_info;
+}
+
+void
+ide_project_file_set_file_info (IdeProjectFile *file,
+                                GFileInfo      *file_info)
+{
+  IdeProjectFilePrivate *priv = ide_project_file_get_instance_private (file);
+
+  g_return_if_fail (IDE_IS_PROJECT_FILE (file));
+  g_return_if_fail (!file_info || G_IS_FILE_INFO (file_info));
+
+  if (g_set_object (&priv->file_info, file_info))
+    g_object_notify_by_pspec (G_OBJECT (file), gParamSpecs [PROP_FILE_INFO]);
+}
+
+static void
+ide_project_file_finalize (GObject *object)
+{
+  IdeProjectFile *self = (IdeProjectFile *)object;
+  IdeProjectFilePrivate *priv = ide_project_file_get_instance_private (self);
+
+  g_clear_object (&priv->file_info);
+
+  G_OBJECT_CLASS (ide_project_file_parent_class)->finalize (object);
+}
+
+static void
+ide_project_file_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  IdeProjectFile *self = IDE_PROJECT_FILE (object);
+
+  switch (prop_id)
+    {
+    case PROP_FILE_INFO:
+      g_value_set_object (value, ide_project_file_get_file_info (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_project_file_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  IdeProjectFile *self = IDE_PROJECT_FILE (object);
+
+  switch (prop_id)
+    {
+    case PROP_FILE_INFO:
+      ide_project_file_set_file_info (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_project_file_class_init (IdeProjectFileClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_project_file_finalize;
+  object_class->get_property = ide_project_file_get_property;
+  object_class->set_property = ide_project_file_set_property;
+
+  gParamSpecs [PROP_FILE_INFO] =
+    g_param_spec_object ("file-info",
+                         _("File Info"),
+                         _("The file info for the project file."),
+                         G_TYPE_FILE_INFO,
+                         (G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_FILE_INFO,
+                                   gParamSpecs [PROP_FILE_INFO]);
+}
+
+static void
+ide_project_file_init (IdeProjectFile *self)
+{
+}
diff --git a/libide/ide-project-file.h b/libide/ide-project-file.h
new file mode 100644
index 0000000..47e69c4
--- /dev/null
+++ b/libide/ide-project-file.h
@@ -0,0 +1,44 @@
+/* ide-project-file.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_PROJECT_FILE_H
+#define IDE_PROJECT_FILE_H
+
+#include <gio/gio.h>
+
+#include "ide-file.h"
+#include "ide-project-item.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_PROJECT_FILE (ide_project_file_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeProjectFile, ide_project_file, IDE, PROJECT_FILE,
+                          IdeProjectItem)
+
+struct _IdeProjectFileClass
+{
+  GObjectClass parent;
+};
+
+IdeFile   *ide_project_file_get_file      (IdeProjectFile *file);
+GFileInfo *ide_project_file_get_file_info (IdeProjectFile *file);
+
+G_END_DECLS
+
+#endif /* IDE_PROJECT_FILE_H */
diff --git a/libide/ide-project-files.c b/libide/ide-project-files.c
new file mode 100644
index 0000000..8ca62f8
--- /dev/null
+++ b/libide/ide-project-files.c
@@ -0,0 +1,31 @@
+/* ide-project-files.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ide-project-files.h"
+
+G_DEFINE_TYPE (IdeProjectFiles, ide_project_files, IDE_TYPE_PROJECT_ITEM)
+
+static void
+ide_project_files_class_init (IdeProjectFilesClass *klass)
+{
+}
+
+static void
+ide_project_files_init (IdeProjectFiles *self)
+{
+}
diff --git a/libide/ide-project-files.h b/libide/ide-project-files.h
new file mode 100644
index 0000000..7d236a5
--- /dev/null
+++ b/libide/ide-project-files.h
@@ -0,0 +1,38 @@
+/* ide-project-files.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_PROJECT_FILES_H
+#define IDE_PROJECT_FILES_H
+
+#include "ide-project-item.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_PROJECT_FILES (ide_project_files_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeProjectFiles, ide_project_files, IDE, PROJECT_FILES,
+                      IdeProjectItem)
+
+struct _IdeProjectFiles
+{
+  IdeProjectItem parent_instance;
+};
+
+G_END_DECLS
+
+#endif /* IDE_PROJECT_FILES_H */
diff --git a/libide/ide-project-item.c b/libide/ide-project-item.c
new file mode 100644
index 0000000..66f236d
--- /dev/null
+++ b/libide/ide-project-item.c
@@ -0,0 +1,176 @@
+/* ide-project-item.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-project-item.h"
+
+typedef struct
+{
+  IdeProjectItem *parent;
+  GSequence      *children;
+} IdeProjectItemPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeProjectItem, ide_project_item, IDE_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_PARENT,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+IdeProjectItem *
+ide_project_item_new (IdeProjectItem *parent)
+{
+  return g_object_new (IDE_TYPE_PROJECT_ITEM,
+                       "parent", parent,
+                       NULL);
+}
+
+void
+ide_project_item_append (IdeProjectItem *item,
+                         IdeProjectItem *child)
+{
+  IdeProjectItemPrivate *priv = ide_project_item_get_instance_private (item);
+
+  g_return_if_fail (IDE_IS_PROJECT_ITEM (item));
+  g_return_if_fail (IDE_IS_PROJECT_ITEM (child));
+
+  if (!priv->children)
+    priv->children = g_sequence_new (g_object_unref);
+
+  g_sequence_append (priv->children, g_object_ref (child));
+}
+
+/**
+ * ide_project_item_get_children:
+ *
+ * A scalable list containing the children of the item.
+ *
+ * Returns: (transfer none): A #GSequence.
+ */
+GSequence *
+ide_project_item_get_children (IdeProjectItem *item)
+{
+  IdeProjectItemPrivate *priv = ide_project_item_get_instance_private (item);
+
+  g_return_val_if_fail (IDE_IS_PROJECT_ITEM (item), NULL);
+
+  return priv->children;
+}
+
+IdeProjectItem *
+ide_project_item_get_parent (IdeProjectItem *item)
+{
+  IdeProjectItemPrivate *priv = ide_project_item_get_instance_private (item);
+
+  g_return_val_if_fail (IDE_IS_PROJECT_ITEM (item), NULL);
+
+  return priv->parent;
+}
+
+void
+ide_project_item_set_parent (IdeProjectItem *item,
+                             IdeProjectItem *parent)
+{
+  IdeProjectItemPrivate *priv = ide_project_item_get_instance_private (item);
+
+  g_return_if_fail (IDE_IS_PROJECT_ITEM (item));
+  g_return_if_fail (!parent || IDE_IS_PROJECT_ITEM (parent));
+
+  if (ide_set_weak_pointer (&priv->parent, parent))
+    g_object_notify_by_pspec (G_OBJECT (item), gParamSpecs [PROP_PARENT]);
+}
+
+static void
+ide_project_item_finalize (GObject *object)
+{
+  IdeProjectItem *self = (IdeProjectItem *)object;
+  IdeProjectItemPrivate *priv = ide_project_item_get_instance_private (self);
+
+  ide_clear_weak_pointer (&priv->parent);
+  g_clear_pointer (&priv->children, g_sequence_free);
+
+  G_OBJECT_CLASS (ide_project_item_parent_class)->finalize (object);
+}
+
+static void
+ide_project_item_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  IdeProjectItem *self = (IdeProjectItem *)object;
+
+  switch (prop_id)
+    {
+    case PROP_PARENT:
+      g_value_set_object (value, ide_project_item_get_parent (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_project_item_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  IdeProjectItem *self = (IdeProjectItem *)object;
+
+  switch (prop_id)
+    {
+    case PROP_PARENT:
+      ide_project_item_set_parent (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_project_item_class_init (IdeProjectItemClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_project_item_finalize;
+  object_class->get_property = ide_project_item_get_property;
+  object_class->set_property = ide_project_item_set_property;
+
+  gParamSpecs [PROP_PARENT] =
+    g_param_spec_object ("parent",
+                         _("Parent"),
+                         _("The parent project item, if not the root."),
+                         IDE_TYPE_PROJECT_ITEM,
+                         (G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_PARENT,
+                                   gParamSpecs [PROP_PARENT]);
+}
+
+static void
+ide_project_item_init (IdeProjectItem *self)
+{
+}
diff --git a/libide/ide-project-item.h b/libide/ide-project-item.h
new file mode 100644
index 0000000..badc8a7
--- /dev/null
+++ b/libide/ide-project-item.h
@@ -0,0 +1,42 @@
+/* ide-project-item.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_PROJECT_ITEM_H
+#define IDE_PROJECT_ITEM_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_PROJECT_ITEM (ide_project_item_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeProjectItem, ide_project_item, IDE, PROJECT_ITEM, IdeObject)
+
+struct _IdeProjectItemClass
+{
+  IdeObjectClass parent_class;
+};
+
+IdeProjectItem *ide_project_item_get_parent   (IdeProjectItem *item);
+void            ide_project_item_append       (IdeProjectItem *item,
+                                               IdeProjectItem *child);
+GSequence      *ide_project_item_get_children (IdeProjectItem *item);
+
+G_END_DECLS
+
+#endif /* IDE_PROJECT_ITEM_H */
diff --git a/libide/ide-project.c b/libide/ide-project.c
new file mode 100644
index 0000000..8a58329
--- /dev/null
+++ b/libide/ide-project.c
@@ -0,0 +1,193 @@
+/* ide-project.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-project.h"
+#include "ide-project-item.h"
+
+typedef struct
+{
+  IdeProjectItem *root;
+  gchar *name;
+} IdeProjectPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeProject, ide_project, IDE_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_NAME,
+  PROP_ROOT,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+const gchar *
+ide_project_get_name (IdeProject *project)
+{
+  IdeProjectPrivate *priv = ide_project_get_instance_private (project);
+
+  g_return_val_if_fail (IDE_IS_PROJECT (project), NULL);
+
+  return priv->name;
+}
+
+void
+ide_project_set_name (IdeProject  *project,
+                      const gchar *name)
+{
+  IdeProjectPrivate *priv = ide_project_get_instance_private (project);
+
+  g_return_if_fail (IDE_IS_PROJECT (project));
+
+  if (priv->name != name)
+    {
+      g_free (priv->name);
+      priv->name = g_strdup (name);
+      g_object_notify_by_pspec (G_OBJECT (project), gParamSpecs [PROP_NAME]);
+    }
+}
+
+IdeProjectItem *
+ide_project_get_root (IdeProject *project)
+{
+  IdeProjectPrivate *priv = ide_project_get_instance_private (project);
+
+  g_return_val_if_fail (IDE_IS_PROJECT (project),  NULL);
+
+  return priv->root;
+}
+
+static void
+ide_project_set_root (IdeProject     *project,
+                      IdeProjectItem *root)
+{
+  IdeProjectPrivate *priv = ide_project_get_instance_private (project);
+  g_autoptr(IdeProjectItem) allocated = NULL;
+  IdeContext *context;
+
+  g_return_if_fail (IDE_IS_PROJECT (project));
+  g_return_if_fail (!root || IDE_IS_PROJECT_ITEM (root));
+
+  context = ide_object_get_context (IDE_OBJECT (project));
+
+  if (!root)
+    {
+      allocated = g_object_new (IDE_TYPE_PROJECT_ITEM,
+                                "context", context,
+                                NULL);
+      root = allocated;
+    }
+
+  if (g_set_object (&priv->root, root))
+    g_object_notify_by_pspec (G_OBJECT (project), gParamSpecs [PROP_ROOT]);
+}
+
+static void
+ide_project_finalize (GObject *object)
+{
+  IdeProject *self = (IdeProject *)object;
+  IdeProjectPrivate *priv = ide_project_get_instance_private (self);
+
+  g_clear_object (&priv->root);
+  g_clear_pointer (&priv->name, g_free);
+
+  G_OBJECT_CLASS (ide_project_parent_class)->finalize (object);
+}
+
+static void
+ide_project_get_property (GObject    *object,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  IdeProject *self = IDE_PROJECT (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      g_value_set_string (value, ide_project_get_name (self));
+      break;
+
+    case PROP_ROOT:
+      g_value_set_object (value, ide_project_get_root (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_project_set_property (GObject      *object,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  IdeProject *self = IDE_PROJECT (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      ide_project_set_name (self, g_value_get_string (value));
+      break;
+
+    case PROP_ROOT:
+      ide_project_set_root (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_project_class_init (IdeProjectClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_project_finalize;
+  object_class->get_property = ide_project_get_property;
+  object_class->set_property = ide_project_set_property;
+
+  gParamSpecs [PROP_NAME] =
+    g_param_spec_string ("name",
+                         _("Name"),
+                         _("The name of the project."),
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_NAME,
+                                   gParamSpecs [PROP_NAME]);
+
+  gParamSpecs [PROP_ROOT] =
+    g_param_spec_object ("root",
+                         _("Root"),
+                         _("The root object for the project."),
+                         IDE_TYPE_PROJECT_ITEM,
+                         (G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_ROOT,
+                                   gParamSpecs [PROP_ROOT]);
+}
+
+static void
+ide_project_init (IdeProject *self)
+{
+}
diff --git a/libide/ide-project.h b/libide/ide-project.h
new file mode 100644
index 0000000..4884023
--- /dev/null
+++ b/libide/ide-project.h
@@ -0,0 +1,42 @@
+/* ide-project.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_PROJECT_H
+#define IDE_PROJECT_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_PROJECT (ide_project_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeProject, ide_project, IDE, PROJECT, IdeObject)
+
+struct _IdeProjectClass
+{
+  IdeObjectClass parent;
+};
+
+IdeProjectItem *ide_project_get_root (IdeProject  *project);
+const gchar    *ide_project_get_name (IdeProject  *project);
+void            ide_project_set_name (IdeProject  *project,
+                                      const gchar *name);
+
+G_END_DECLS
+
+#endif /* IDE_PROJECT_H */
diff --git a/libide/ide-refactory.h b/libide/ide-refactory.h
new file mode 100644
index 0000000..e6bc25c
--- /dev/null
+++ b/libide/ide-refactory.h
@@ -0,0 +1,40 @@
+/* ide-refactory.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_REFACTORY_H
+#define IDE_REFACTORY_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_REFACTORY               (ide_refactory_get_type ())
+#define IDE_REFACTORY(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_REFACTORY, 
IdeRefactory))
+#define IDE_IS_REFACTORY(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_REFACTORY))
+#define IDE_REFACTORY_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), IDE_TYPE_REFACTORY, 
IdeRefactoryInterface))
+
+struct _IdeRefactoryInterface
+{
+  GTypeInterface parent;
+};
+
+GType ide_refactory_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_REFACTORY_H */
diff --git a/libide/ide-script.c b/libide/ide-script.c
new file mode 100644
index 0000000..0fb85bf
--- /dev/null
+++ b/libide/ide-script.c
@@ -0,0 +1,78 @@
+/* ide-script.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-script.h"
+
+G_DEFINE_TYPE (IdeScript, ide_script, IDE_TYPE_OBJECT)
+
+enum {
+  LOAD,
+  UNLOAD,
+  LAST_SIGNAL
+};
+
+static guint gSignals [LAST_SIGNAL];
+
+void
+ide_script_load (IdeScript *script)
+{
+  g_return_if_fail (IDE_IS_SCRIPT (script));
+
+  g_signal_emit (script, gSignals [LOAD], 0);
+}
+
+void
+ide_script_unload (IdeScript *script)
+{
+  g_return_if_fail (IDE_IS_SCRIPT (script));
+
+  g_signal_emit (script, gSignals [UNLOAD], 0);
+}
+
+static void
+ide_script_class_init (IdeScriptClass *klass)
+{
+  gSignals [LOAD] =
+    g_signal_new ("load",
+                  IDE_TYPE_SCRIPT,
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (IdeScriptClass, load),
+                  NULL,
+                  NULL,
+                  g_cclosure_marshal_generic,
+                  G_TYPE_NONE,
+                  0);
+
+  gSignals [UNLOAD] =
+    g_signal_new ("unload",
+                  IDE_TYPE_SCRIPT,
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (IdeScriptClass, unload),
+                  NULL,
+                  NULL,
+                  g_cclosure_marshal_generic,
+                  G_TYPE_NONE,
+                  0);
+}
+
+static void
+ide_script_init (IdeScript *self)
+{
+}
diff --git a/libide/ide-script.h b/libide/ide-script.h
new file mode 100644
index 0000000..7da358e
--- /dev/null
+++ b/libide/ide-script.h
@@ -0,0 +1,43 @@
+/* ide-script.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_SCRIPT_H
+#define IDE_SCRIPT_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SCRIPT (ide_script_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeScript, ide_script, IDE, SCRIPT, IdeObject)
+
+struct _IdeScriptClass
+{
+  IdeObjectClass parent;
+
+  void (*load)   (IdeScript *script);
+  void (*unload) (IdeScript *script);
+};
+
+void ide_script_load   (IdeScript *script);
+void ide_script_unload (IdeScript *script);
+
+G_END_DECLS
+
+#endif /* IDE_SCRIPT_H */
diff --git a/libide/ide-search-engine.h b/libide/ide-search-engine.h
new file mode 100644
index 0000000..d2035b3
--- /dev/null
+++ b/libide/ide-search-engine.h
@@ -0,0 +1,37 @@
+/* ide-search-engine.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_SEARCH_ENGINE_H
+#define IDE_SEARCH_ENGINE_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SEARCH_ENGINE (ide_search_engine_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeSearchEngine, ide_search_engine, IDE, SEARCH_ENGINE, IdeObject)
+
+struct _IdeSearchEngine
+{
+  IdeObject parent_instance;
+};
+
+G_END_DECLS
+
+#endif /* IDE_SEARCH_ENGINE_H */
diff --git a/libide/ide-search-provider.h b/libide/ide-search-provider.h
new file mode 100644
index 0000000..9483699
--- /dev/null
+++ b/libide/ide-search-provider.h
@@ -0,0 +1,37 @@
+/* ide-search-provider.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_SEARCH_PROVIDER_H
+#define IDE_SEARCH_PROVIDER_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SEARCH_PROVIDER (ide_search_provider_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeSearchProvider, ide_search_provider, IDE, SEARCH_PROVIDER, IdeObject)
+
+struct _IdeSearchProviderClass
+{
+  IdeObjectClass parent_class;
+};
+
+G_END_DECLS
+
+#endif /* IDE_SEARCH_PROVIDER_H */
diff --git a/libide/ide-search-result.h b/libide/ide-search-result.h
new file mode 100644
index 0000000..00d850e
--- /dev/null
+++ b/libide/ide-search-result.h
@@ -0,0 +1,37 @@
+/* ide-search-result.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_SEARCH_RESULT_H
+#define IDE_SEARCH_RESULT_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SEARCH_RESULT (ide_search_result_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeSearchResult, ide_search_result, IDE, SEARCH_RESULT, IdeObject)
+
+struct _IdeSearchResultClass
+{
+  IdeObjectClass parent;
+};
+
+G_END_DECLS
+
+#endif /* IDE_SEARCH_RESULT_H */
diff --git a/libide/ide-service.c b/libide/ide-service.c
new file mode 100644
index 0000000..09df89c
--- /dev/null
+++ b/libide/ide-service.c
@@ -0,0 +1,144 @@
+/* ide-service.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-service.h"
+
+typedef struct
+{
+  guint running : 1;
+} IdeServicePrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (IdeService, ide_service, IDE_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_NAME,
+  PROP_RUNNING,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+gboolean
+ide_service_get_running (IdeService *service)
+{
+  IdeServicePrivate *priv = ide_service_get_instance_private (service);
+
+  g_return_val_if_fail (IDE_IS_SERVICE (service), FALSE);
+
+  return priv->running;
+}
+
+const gchar *
+ide_service_get_name (IdeService *service)
+{
+  g_return_val_if_fail (IDE_IS_SERVICE (service), NULL);
+
+  if (IDE_SERVICE_GET_CLASS (service)->get_name)
+    return IDE_SERVICE_GET_CLASS (service)->get_name (service);
+
+  return NULL;
+}
+
+void
+ide_service_start (IdeService *service)
+{
+  IdeServicePrivate *priv = ide_service_get_instance_private (service);
+
+  g_return_if_fail (IDE_IS_SERVICE (service));
+
+  if (!priv->running)
+    {
+      if (IDE_SERVICE_GET_CLASS (service)->start)
+        IDE_SERVICE_GET_CLASS (service)->start (service);
+
+      priv->running = TRUE;
+    }
+}
+
+void
+ide_service_stop (IdeService *service)
+{
+  IdeServicePrivate *priv = ide_service_get_instance_private (service);
+
+  g_return_if_fail (IDE_IS_SERVICE (service));
+
+  if (priv->running)
+    {
+      if (IDE_SERVICE_GET_CLASS (service)->stop)
+        IDE_SERVICE_GET_CLASS (service)->stop (service);
+
+      priv->running = FALSE;
+    }
+}
+
+static void
+ide_service_get_property (GObject    *object,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  IdeService *self = IDE_SERVICE (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      g_value_set_string (value, ide_service_get_name (self));
+      break;
+
+    case PROP_RUNNING:
+      g_value_set_boolean (value, ide_service_get_running (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_service_class_init (IdeServiceClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = ide_service_get_property;
+
+  gParamSpecs [PROP_NAME] =
+    g_param_spec_string ("name",
+                         _("Name"),
+                         _("The name of the service."),
+                         NULL,
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_NAME,
+                                   gParamSpecs [PROP_NAME]);
+
+  gParamSpecs [PROP_RUNNING] =
+    g_param_spec_boolean ("running",
+                          _("Running"),
+                          _("If the service is running."),
+                          FALSE,
+                          (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_RUNNING,
+                                   gParamSpecs [PROP_RUNNING]);
+}
+
+static void
+ide_service_init (IdeService *self)
+{
+}
diff --git a/libide/ide-service.h b/libide/ide-service.h
new file mode 100644
index 0000000..8bfa618
--- /dev/null
+++ b/libide/ide-service.h
@@ -0,0 +1,46 @@
+/* ide-service.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_SERVICE_H
+#define IDE_SERVICE_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SERVICE (ide_service_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeService, ide_service, IDE, SERVICE, IdeObject)
+
+struct _IdeServiceClass
+{
+  IdeObjectClass parent;
+
+  const gchar *(*get_name) (IdeService *service);
+  void         (*start)    (IdeService *service);
+  void         (*stop)     (IdeService *service);
+};
+
+const gchar *ide_service_get_name    (IdeService *service);
+gboolean     ide_service_get_running (IdeService *service);
+void         ide_service_start       (IdeService *service);
+void         ide_service_stop        (IdeService *service);
+
+G_END_DECLS
+
+#endif /* IDE_SERVICE_H */
diff --git a/libide/ide-symbol-resolver.h b/libide/ide-symbol-resolver.h
new file mode 100644
index 0000000..d6104bf
--- /dev/null
+++ b/libide/ide-symbol-resolver.h
@@ -0,0 +1,40 @@
+/* ide-symbol-resolver.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_SYMBOL_RESOLVER_H
+#define IDE_SYMBOL_RESOLVER_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SYMBOL_RESOLVER               (ide_symbol_resolver_get_type ())
+#define IDE_SYMBOL_RESOLVER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_SYMBOL_RESOLVER, 
IdeSymbolResolver))
+#define IDE_IS_SYMBOL_RESOLVER(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_SYMBOL_RESOLVER))
+#define IDE_SYMBOL_RESOLVER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), 
IDE_TYPE_SYMBOL_RESOLVER, IdeSymbolResolverInterface))
+
+struct _IdeSymbolResolverInterface
+{
+  GTypeInterface parent;
+};
+
+GType ide_symbol_resolver_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_SYMBOL_RESOLVER_H */
diff --git a/libide/ide-symbol.h b/libide/ide-symbol.h
new file mode 100644
index 0000000..667184a
--- /dev/null
+++ b/libide/ide-symbol.h
@@ -0,0 +1,37 @@
+/* ide-symbol.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_SYMBOL_H
+#define IDE_SYMBOL_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SYMBOL (ide_symbol_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeSymbol, ide_symbol, IDE, SYMBOL, IdeObject)
+
+struct _IdeSymbolClass
+{
+  IdeObjectClass parent;
+};
+
+G_END_DECLS
+
+#endif /* IDE_SYMBOL_H */
diff --git a/libide/ide-target.h b/libide/ide-target.h
new file mode 100644
index 0000000..d72ea1c
--- /dev/null
+++ b/libide/ide-target.h
@@ -0,0 +1,40 @@
+/* ide-target.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_TARGET_H
+#define IDE_TARGET_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_TARGET               (ide_target_get_type ())
+#define IDE_TARGET(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_TARGET, IdeTarget))
+#define IDE_IS_TARGET(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_TARGET))
+#define IDE_TARGET_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), IDE_TYPE_TARGET, 
IdeTargetInterface))
+
+struct _IdeTargetInterface
+{
+  GTypeInterface parent;
+};
+
+GType ide_target_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_TARGET_H */
diff --git a/libide/ide-test-case.h b/libide/ide-test-case.h
new file mode 100644
index 0000000..8d8c908
--- /dev/null
+++ b/libide/ide-test-case.h
@@ -0,0 +1,40 @@
+/* ide-test-case.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_TEST_CASE_H
+#define IDE_TEST_CASE_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_TEST_CASE               (ide_test_case_get_type ())
+#define IDE_TEST_CASE(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_TEST_CASE, 
IdeTestCase))
+#define IDE_IS_TEST_CASE(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_TEST_CASE))
+#define IDE_TEST_CASE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), IDE_TYPE_TEST_CASE, 
IdeTestCaseInterface))
+
+struct _IdeTestCaseInterface
+{
+  GTypeInterface parent;
+};
+
+GType ide_test_case_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_TEST_CASE_H */
diff --git a/libide/ide-test-suite.h b/libide/ide-test-suite.h
new file mode 100644
index 0000000..59a097d
--- /dev/null
+++ b/libide/ide-test-suite.h
@@ -0,0 +1,40 @@
+/* ide-test-suite.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_TEST_SUITE_H
+#define IDE_TEST_SUITE_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_TEST_SUITE               (ide_test_suite_get_type ())
+#define IDE_TEST_SUITE(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_TEST_SUITE, 
IdeTestSuite))
+#define IDE_IS_TEST_SUITE(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_TEST_SUITE))
+#define IDE_TEST_SUITE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), IDE_TYPE_TEST_SUITE, 
IdeTestSuiteInterface))
+
+struct _IdeTestSuiteInterface
+{
+  GTypeInterface parent;
+};
+
+GType ide_test_suite_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_TEST_SUITE_H */
diff --git a/libide/ide-types.h b/libide/ide-types.h
new file mode 100644
index 0000000..e4c3be1
--- /dev/null
+++ b/libide/ide-types.h
@@ -0,0 +1,127 @@
+/* ide-types.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_TYPES_H
+#define IDE_TYPES_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define ide_clear_weak_pointer(ptr) \
+  (*(ptr) ? (g_object_remove_weak_pointer((GObject*)*(ptr), (gpointer*)ptr),1) : 0)
+
+#define ide_set_weak_pointer(ptr,obj) \
+  ((obj!=*(ptr)) ? (ide_clear_weak_pointer(ptr),*(ptr)=obj,g_object_add_weak_pointer((GObject*)obj, 
(gpointer*)ptr),1) : 0)
+
+typedef struct _IdeBuffer                      IdeBuffer;
+typedef struct _IdeBufferInterface             IdeBufferInterface;
+
+typedef struct _IdeBufferIter                  IdeBufferIter;
+typedef struct _IdeBufferIterInterface         IdeBufferIterInterface;
+
+typedef struct _IdeBuilder                     IdeBuilder;
+
+typedef struct _IdeBuildResult                 IdeBuildResult;
+
+typedef struct _IdeBuildSystem                 IdeBuildSystem;
+
+typedef struct _IdeContext                     IdeContext;
+
+typedef struct _IdeDebugger                    IdeDebugger;
+typedef struct _IdeDebuggerInterface           IdeDebuggerInterface;
+
+typedef struct _IdeDeployer                    IdeDeployer;
+
+typedef struct _IdeDevice                      IdeDevice;
+
+typedef struct _IdeDeviceManager               IdeDeviceManager;
+
+typedef struct _IdeDeviceProvider              IdeDeviceProvider;
+typedef struct _IdeDeviceProviderInterface     IdeDeviceProviderInterface;
+
+typedef struct _IdeDiagnostic                  IdeDiagnostic;
+typedef struct _IdeDiagnosticInterface         IdeDiagnosticInterface;
+
+typedef struct _IdeDiagnosticProvider          IdeDiagnosticProvider;
+typedef struct _IdeDiagnosticProviderInterface IdeDiagnosticProviderInterface;
+
+typedef struct _IdeExecuter                    IdeExecuter;
+typedef struct _IdeExecuterInterface           IdeExecuterInterface;
+
+typedef struct _IdeExecutable                  IdeExecutable;
+typedef struct _IdeExecutableInterface         IdeExecutableInterface;
+
+typedef struct _IdeFile                        IdeFile;
+
+typedef struct _IdeIndenter                    IdeIndenter;
+typedef struct _IdeIndenterInterface           IdeIndenterInterface;
+
+typedef struct _IdeLanguage                    IdeLanguage;
+typedef struct _IdeLanguageInterface           IdeLanguageInterface;
+
+typedef struct _IdeObject                      IdeObject;
+
+typedef struct _IdeProcess                     IdeProcess;
+typedef struct _IdeProcessInterface            IdeProcessInterface;
+
+typedef struct _IdeProject                     IdeProject;
+
+typedef struct _IdeProjectItem                 IdeProjectItem;
+
+typedef struct _IdeProjectFile                 IdeProjectFile;
+
+typedef struct _IdeProjectFiles                IdeProjectFiles;
+
+typedef struct _IdeRefactory                   IdeRefactory;
+typedef struct _IdeRefactoryInterface          IdeRefactoryInterface;
+
+typedef struct _IdeScript                      IdeScript;
+typedef struct _IdeScriptClass                 IdeScriptClass;
+typedef struct _IdeScriptPrivate               IdeScriptPrivate;
+
+typedef struct _IdeSearchEngine                IdeSearchEngine;
+
+typedef struct _IdeSearchProvider              IdeSearchProvider;
+
+typedef struct _IdeSearchResult                IdeSearchResult;
+typedef struct _IdeSearchResultClass           IdeSearchResultClass;
+typedef struct _IdeSearchResultPrivate         IdeSearchResultPrivate;
+
+typedef struct _IdeService                     IdeService;
+
+typedef struct _IdeSymbol                      IdeSymbol;
+
+typedef struct _IdeSymbolResolver              IdeSymbolResolver;
+typedef struct _IdeSymbolResolverInterface     IdeSymbolResolverInterface;
+
+typedef struct _IdeTarget                      IdeTarget;
+typedef struct _IdeTargetInterface             IdeTargetInterface;
+
+typedef struct _IdeTestCase                    IdeTestCase;
+typedef struct _IdeTestCaseInterface           IdeTestCaseInterface;
+
+typedef struct _IdeTestSuite                   IdeTestSuite;
+typedef struct _IdeTestSuiteInterface          IdeTestSuiteInterface;
+
+typedef struct _IdeUnsavedFiles                IdeUnsavedFiles;
+typedef struct _IdeVcs                         IdeVcs;
+
+G_END_DECLS
+
+#endif /* IDE_TYPES_H */
diff --git a/libide/ide-unsaved-files.c b/libide/ide-unsaved-files.c
new file mode 100644
index 0000000..ee682fc
--- /dev/null
+++ b/libide/ide-unsaved-files.c
@@ -0,0 +1,474 @@
+/* ide-unsaved-files.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+
+#include "ide-context.h"
+#include "ide-global.h"
+#include "ide-project.h"
+#include "ide-unsaved-files.h"
+
+typedef struct
+{
+  GFile  *file;
+  GBytes *content;
+} UnsavedFile;
+
+typedef struct
+{
+  GPtrArray *unsaved_files;
+} IdeUnsavedFilesPrivate;
+
+typedef struct
+{
+  GPtrArray *unsaved_files;
+  gchar     *drafts_directory;
+} AsyncState;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeUnsavedFiles, ide_unsaved_files, IDE_TYPE_OBJECT)
+
+static void
+async_state_free (gpointer data)
+{
+  AsyncState *state = data;
+
+  if (state)
+    {
+      g_free (state->drafts_directory);
+      g_ptr_array_unref (state->unsaved_files);
+      g_slice_free (AsyncState, state);
+    }
+}
+
+static void
+unsaved_file_free (gpointer data)
+{
+  UnsavedFile *uf = data;
+
+  if (uf)
+    {
+      g_object_unref (uf->file);
+      g_bytes_unref (uf->content);
+      g_slice_free (UnsavedFile, uf);
+    }
+}
+
+static UnsavedFile *
+unsaved_file_copy (const UnsavedFile *uf)
+{
+  UnsavedFile *copy;
+
+  copy = g_slice_new0 (UnsavedFile);
+  copy->file = g_object_ref (uf->file);
+  copy->content = g_bytes_ref (uf->content);
+
+  return copy;
+}
+
+static gboolean
+unsaved_file_save (UnsavedFile  *uf,
+                   const gchar  *path,
+                   GError      **error)
+{
+  gboolean ret;
+
+  ret = g_file_set_contents (path,
+                             g_bytes_get_data (uf->content, NULL),
+                             g_bytes_get_size (uf->content),
+                             error);
+  return ret;
+}
+
+static gchar *
+hash_uri (const gchar *uri)
+{
+  GChecksum *checksum;
+  gchar *ret;
+
+  checksum = g_checksum_new (G_CHECKSUM_SHA1);
+  g_checksum_update (checksum, (guchar *)uri, strlen (uri));
+  ret = g_strdup (g_checksum_get_string (checksum));
+  g_checksum_free (checksum);
+
+  return ret;
+}
+
+static void
+ide_unsaved_files_save_worker (GTask        *task,
+                               gpointer      source_object,
+                               gpointer      task_data,
+                               GCancellable *cancellable)
+{
+  GString *manifest;
+  AsyncState *state = task_data;
+  g_autoptr(gchar) manifest_path = NULL;
+  GError *error = NULL;
+  gsize i;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (IDE_IS_UNSAVED_FILES (source_object));
+  g_assert (state);
+
+  manifest = g_string_new (NULL);
+  manifest_path = g_build_filename (state->drafts_directory,
+                                    "manifest",
+                                    NULL);
+
+  for (i = 0; i < state->unsaved_files->len; i++)
+    {
+      g_autoptr(gchar) path = NULL;
+      g_autoptr(gchar) uri = NULL;
+      g_autoptr(gchar) hash = NULL;
+      UnsavedFile *uf;
+
+      uf = g_ptr_array_index (state->unsaved_files, i);
+
+      uri = g_file_get_uri (uf->file);
+
+      g_string_append_printf (manifest, "%s\n", uri);
+
+      hash = hash_uri (uri);
+      path = g_build_filename (state->drafts_directory, hash, NULL);
+
+      if (!unsaved_file_save (uf, path, &error))
+        {
+          g_task_return_error (task, error);
+          goto cleanup;
+        }
+    }
+
+  if (!g_file_set_contents (manifest_path,
+                            manifest->str, manifest->len,
+                            &error))
+    {
+      g_task_return_error (task, error);
+      goto cleanup;
+    }
+
+  g_task_return_boolean (task, TRUE);
+
+cleanup:
+  g_string_free (manifest, TRUE);
+}
+
+static AsyncState *
+async_state_new (IdeUnsavedFiles *files)
+{
+  IdeContext *context;
+  IdeProject *project;
+  AsyncState *state;
+  const gchar *project_name;
+
+  g_assert (IDE_IS_UNSAVED_FILES (files));
+
+  context = ide_object_get_context (IDE_OBJECT (files));
+  project = ide_context_get_project (context);
+  project_name = ide_project_get_name (project);
+
+  state = g_slice_new (AsyncState);
+  state->unsaved_files = g_ptr_array_new_with_free_func (unsaved_file_free);
+  state->drafts_directory = g_build_filename (g_get_user_data_dir (),
+                                              ide_get_program_name (),
+                                              "drafts",
+                                              project_name,
+                                              NULL);
+
+  return state;
+}
+
+void
+ide_unsaved_files_save_async (IdeUnsavedFiles     *files,
+                              GCancellable        *cancellable,
+                              GAsyncReadyCallback  callback,
+                              gpointer             user_data)
+{
+  IdeUnsavedFilesPrivate *priv;
+  g_autoptr(GTask) task;
+  AsyncState *state;
+  gsize i;
+
+  g_return_if_fail (IDE_IS_UNSAVED_FILES (files));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  priv = ide_unsaved_files_get_instance_private (files);
+
+  state = async_state_new (files);
+
+  for (i = 0; i < priv->unsaved_files->len; i++)
+    {
+      UnsavedFile *uf;
+      UnsavedFile *uf_copy;
+
+      uf = g_ptr_array_index (priv->unsaved_files, i);
+      uf_copy = unsaved_file_copy (uf);
+      g_ptr_array_add (state->unsaved_files, uf_copy);
+    }
+
+  task = g_task_new (files, cancellable, callback, user_data);
+  g_task_set_task_data (task, state, async_state_free);
+  g_task_run_in_thread (task, ide_unsaved_files_save_worker);
+}
+
+gboolean
+ide_unsaved_files_save_finish (IdeUnsavedFiles  *files,
+                               GAsyncResult     *result,
+                               GError          **error)
+{
+  g_return_val_if_fail (IDE_IS_UNSAVED_FILES (files), FALSE);
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+ide_unsaved_files_restore_worker (GTask        *task,
+                                  gpointer      source_object,
+                                  gpointer      task_data,
+                                  GCancellable *cancellable)
+{
+  AsyncState *state = task_data;
+  g_autoptr(gchar) contents = NULL;
+  g_autoptr(gchar) manifest_path = NULL;
+  gchar **lines;
+  GError *error = NULL;
+  gsize len;
+  gsize i;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (IDE_IS_UNSAVED_FILES (source_object));
+  g_assert (state);
+
+  manifest_path = g_build_filename (state->drafts_directory,
+                                    "manifest",
+                                    NULL);
+
+  if (!g_file_test (manifest_path, G_FILE_TEST_IS_REGULAR))
+    {
+      g_task_return_boolean (task, TRUE);
+      return;
+    }
+
+  if (!g_file_get_contents (manifest_path, &contents, &len, &error))
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  lines = g_strsplit (contents, "\n", 0);
+
+  for (i = 0; lines [i]; i++)
+    {
+      g_autoptr(GFile) file = NULL;
+      g_autoptr(gchar) contents = NULL;
+      g_autoptr(gchar) hash = NULL;
+      g_autoptr(gchar) path = NULL;
+      UnsavedFile *unsaved;
+      gsize len;
+
+      if (!*lines [i])
+        continue;
+
+      file = g_file_new_for_uri (lines [i]);
+      if (!file)
+        continue;
+
+      hash = hash_uri (lines [i]);
+      path = g_build_filename (state->drafts_directory, hash, NULL);
+
+      if (!g_file_get_contents (path, &contents, &len, &error))
+        {
+          g_task_return_error (task, error);
+          break;
+        }
+
+      unsaved = g_slice_new0 (UnsavedFile);
+      unsaved->file = g_object_ref (file);
+      unsaved->content = g_bytes_new_take (contents, len);
+
+      g_ptr_array_add (state->unsaved_files, unsaved);
+    }
+
+  g_strfreev (lines);
+}
+
+void
+ide_unsaved_files_restore_async (IdeUnsavedFiles     *files,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
+{
+  g_autoptr(GTask) task;
+  AsyncState *state;
+
+  g_return_if_fail (IDE_IS_UNSAVED_FILES (files));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+  g_return_if_fail (callback);
+
+  state = async_state_new (files);
+
+  task = g_task_new (files, cancellable, callback, user_data);
+  g_task_set_task_data (task, state, async_state_free);
+  g_task_run_in_thread (task, ide_unsaved_files_restore_worker);
+}
+
+gboolean
+ide_unsaved_files_restore_finish (IdeUnsavedFiles  *files,
+                                  GAsyncResult     *result,
+                                  GError          **error)
+{
+  AsyncState *state;
+  gsize i;
+
+  g_return_val_if_fail (IDE_IS_UNSAVED_FILES (files), FALSE);
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+  state = g_task_get_task_data (G_TASK (result));
+
+  for (i = 0; i < state->unsaved_files->len; i++)
+    {
+      UnsavedFile *uf;
+
+      uf = g_ptr_array_index (state->unsaved_files, i);
+      ide_unsaved_files_update (files, uf->file, uf->content);
+    }
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+ide_unsaved_files_move_to_front (IdeUnsavedFiles *self,
+                                 guint            index)
+{
+  IdeUnsavedFilesPrivate *priv = ide_unsaved_files_get_instance_private (self);
+  UnsavedFile *new_front;
+  UnsavedFile *old_front;
+
+  g_return_if_fail (IDE_IS_UNSAVED_FILES (self));
+
+  new_front = g_ptr_array_index (priv->unsaved_files, index);
+  old_front = g_ptr_array_index (priv->unsaved_files, 0);
+
+  /*
+   * TODO: We could shift all these items down, but it probably isnt' worth
+   *       the effort. We will just move-to-front after a miss and ping
+   *       pong the old item back to the front.
+   */
+  priv->unsaved_files->pdata[0] = new_front;
+  priv->unsaved_files->pdata[index] = old_front;
+}
+
+void
+ide_unsaved_files_remove (IdeUnsavedFiles *self,
+                          GFile           *file)
+{
+  IdeUnsavedFilesPrivate *priv = ide_unsaved_files_get_instance_private (self);
+  guint i;
+
+  g_return_if_fail (IDE_IS_UNSAVED_FILES (self));
+  g_return_if_fail (G_IS_FILE (file));
+
+  for (i = 0; i < priv->unsaved_files->len; i++)
+    {
+      UnsavedFile *unsaved;
+
+      unsaved = g_ptr_array_index (priv->unsaved_files, i);
+
+      if (g_file_equal (file, unsaved->file))
+        {
+          g_ptr_array_remove_index_fast (priv->unsaved_files, i);
+          break;
+        }
+    }
+}
+
+void
+ide_unsaved_files_update (IdeUnsavedFiles *self,
+                          GFile           *file,
+                          GBytes          *content)
+{
+  IdeUnsavedFilesPrivate *priv = ide_unsaved_files_get_instance_private (self);
+  UnsavedFile *unsaved;
+  guint i;
+
+  g_return_if_fail (IDE_IS_UNSAVED_FILES (self));
+  g_return_if_fail (G_IS_FILE (file));
+
+  if (!content)
+    {
+      ide_unsaved_files_remove (self, file);
+      return;
+    }
+
+  for (i = 0; i < priv->unsaved_files->len; i++)
+    {
+      unsaved = g_ptr_array_index (priv->unsaved_files, i);
+
+      if (g_file_equal (file, unsaved->file))
+        {
+          if (content != unsaved->content)
+            {
+              g_clear_pointer (&unsaved->content, g_bytes_unref);
+              unsaved->content = g_bytes_ref (content);
+            }
+
+          /*
+           * A file that get's updated is the most likely to get updated on
+           * the next attempt. Therefore, we will simply move this entry to
+           * the beginning of the array to increase it's chances of being the
+           * first entry we check.
+           */
+          if (i != 0)
+            ide_unsaved_files_move_to_front (self, i);
+
+          return;
+        }
+    }
+
+  unsaved = g_slice_new0 (UnsavedFile);
+  unsaved->file = g_object_ref (file);
+  unsaved->content = g_bytes_ref (content);
+
+  g_ptr_array_insert (priv->unsaved_files, 0, unsaved);
+}
+
+static void
+ide_unsaved_files_finalize (GObject *object)
+{
+  IdeUnsavedFiles *self = (IdeUnsavedFiles *)object;
+  IdeUnsavedFilesPrivate *priv = ide_unsaved_files_get_instance_private (self);
+
+  g_clear_pointer (&priv->unsaved_files, g_ptr_array_unref);
+
+  G_OBJECT_CLASS (ide_unsaved_files_parent_class)->finalize (object);
+}
+
+static void
+ide_unsaved_files_class_init (IdeUnsavedFilesClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_unsaved_files_finalize;
+}
+
+static void
+ide_unsaved_files_init (IdeUnsavedFiles *self)
+{
+  IdeUnsavedFilesPrivate *priv = ide_unsaved_files_get_instance_private (self);
+
+  priv->unsaved_files = g_ptr_array_new_with_free_func (unsaved_file_free);
+}
diff --git a/libide/ide-unsaved-files.h b/libide/ide-unsaved-files.h
new file mode 100644
index 0000000..a9f8d3e
--- /dev/null
+++ b/libide/ide-unsaved-files.h
@@ -0,0 +1,57 @@
+/* ide-unsaved-files.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_UNSAVED_FILES_H
+#define IDE_UNSAVED_FILES_H
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_UNSAVED_FILES (ide_unsaved_files_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeUnsavedFiles, ide_unsaved_files, IDE, UNSAVED_FILES, IdeObject)
+
+struct _IdeUnsavedFiles
+{
+  IdeObject parent_instance;
+};
+
+void     ide_unsaved_files_update         (IdeUnsavedFiles      *self,
+                                           GFile                *file,
+                                           GBytes               *content);
+void     ide_unsaved_files_remove         (IdeUnsavedFiles      *self,
+                                           GFile                *file);
+void     ide_unsaved_files_save_async     (IdeUnsavedFiles      *files,
+                                           GCancellable         *cancellable,
+                                           GAsyncReadyCallback   callback,
+                                           gpointer              user_data);
+gboolean ide_unsaved_files_save_finish    (IdeUnsavedFiles      *files,
+                                           GAsyncResult         *result,
+                                           GError              **error);
+void     ide_unsaved_files_restore_async  (IdeUnsavedFiles      *files,
+                                           GCancellable         *cancellable,
+                                           GAsyncReadyCallback   callback,
+                                           gpointer              user_data);
+gboolean ide_unsaved_files_restore_finish (IdeUnsavedFiles      *files,
+                                           GAsyncResult         *result,
+                                           GError              **error);
+
+G_END_DECLS
+
+#endif /* IDE_UNSAVED_FILES_H */
diff --git a/libide/ide-vcs.c b/libide/ide-vcs.c
new file mode 100644
index 0000000..8eb64a0
--- /dev/null
+++ b/libide/ide-vcs.c
@@ -0,0 +1,60 @@
+/* ide-vcs.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ide-vcs.h"
+
+G_DEFINE_ABSTRACT_TYPE (IdeVcs, ide_vcs, IDE_TYPE_OBJECT)
+
+static void
+ide_vcs_class_init (IdeVcsClass *klass)
+{
+}
+
+static void
+ide_vcs_init (IdeVcs *self)
+{
+}
+
+void
+ide_vcs_new_async (IdeContext           *context,
+                   int                   io_priority,
+                   GCancellable         *cancellable,
+                   GAsyncReadyCallback   callback,
+                   gpointer              user_data)
+{
+  ide_object_new_async (IDE_VCS_EXTENSION_POINT,
+                        io_priority,
+                        cancellable,
+                        callback,
+                        user_data,
+                        "context", context,
+                        NULL);
+}
+
+IdeVcs *
+ide_vcs_new_finish (GAsyncResult  *result,
+                    GError       **error)
+{
+  IdeObject *ret;
+
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+
+  ret = ide_object_new_finish (result, error);
+
+  return IDE_VCS (ret);
+}
diff --git a/libide/ide-vcs.h b/libide/ide-vcs.h
new file mode 100644
index 0000000..e5e0c9d
--- /dev/null
+++ b/libide/ide-vcs.h
@@ -0,0 +1,48 @@
+/* ide-vcs.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_VCS_H
+#define IDE_VCS_H
+
+#include <gio/gio.h>
+
+#include "ide-object.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_VCS            (ide_vcs_get_type())
+#define IDE_VCS_EXTENSION_POINT "org.gnome.libide.extensions.vcs"
+
+G_DECLARE_DERIVABLE_TYPE (IdeVcs, ide_vcs, IDE, VCS, IdeObject)
+
+struct _IdeVcsClass
+{
+  IdeObjectClass parent;
+};
+
+void    ide_vcs_new_async  (IdeContext           *context,
+                            int                   io_priority,
+                            GCancellable         *cancellable,
+                            GAsyncReadyCallback   callback,
+                            gpointer              user_data);
+IdeVcs *ide_vcs_new_finish (GAsyncResult         *result,
+                            GError              **error);
+
+G_END_DECLS
+
+#endif /* IDE_VCS_H */
diff --git a/libide/ide.c b/libide/ide.c
new file mode 100644
index 0000000..2ad6f5b
--- /dev/null
+++ b/libide/ide.c
@@ -0,0 +1,90 @@
+/* ide.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+#include <libgit2-glib/ggit.h>
+
+#include "gconstructor.h"
+#include "ide.h"
+#include "autotools/ide-autotools-build-system.h"
+#include "directory/ide-directory-build-system.h"
+#include "directory/ide-directory-vcs.h"
+#include "git/ide-git-vcs.h"
+
+static gboolean     gProgramNameRead;
+static const gchar *gProgramName = "libide";
+
+const gchar *
+ide_get_program_name (void)
+{
+  gProgramNameRead = 1;
+  return gProgramName;
+}
+
+void
+ide_set_program_name (const gchar *program_name)
+{
+  if (gProgramNameRead)
+    {
+      g_warning (_("You must call %s() before using libide."),
+                 G_STRFUNC);
+      return;
+    }
+
+  gProgramName = g_intern_string (program_name);
+}
+
+static void
+ide_init_ctor (void)
+{
+  g_type_ensure (IDE_TYPE_CONTEXT);
+  g_type_ensure (IDE_TYPE_BUILD_SYSTEM);
+  g_type_ensure (IDE_TYPE_VCS);
+
+  g_io_extension_point_register (IDE_BUILD_SYSTEM_EXTENSION_POINT);
+  g_io_extension_point_register (IDE_VCS_EXTENSION_POINT);
+
+  g_io_extension_point_implement (IDE_BUILD_SYSTEM_EXTENSION_POINT,
+                                  IDE_TYPE_AUTOTOOLS_BUILD_SYSTEM,
+                                  IDE_BUILD_SYSTEM_EXTENSION_POINT".autotools",
+                                  -100);
+  g_io_extension_point_implement (IDE_BUILD_SYSTEM_EXTENSION_POINT,
+                                  IDE_TYPE_DIRECTORY_BUILD_SYSTEM,
+                                  IDE_BUILD_SYSTEM_EXTENSION_POINT".directory",
+                                  -200);
+
+  g_io_extension_point_implement (IDE_VCS_EXTENSION_POINT,
+                                  IDE_TYPE_GIT_VCS,
+                                  IDE_VCS_EXTENSION_POINT".git",
+                                  -100);
+  g_io_extension_point_implement (IDE_VCS_EXTENSION_POINT,
+                                  IDE_TYPE_DIRECTORY_VCS,
+                                  IDE_VCS_EXTENSION_POINT".directory",
+                                  -200);
+
+  ggit_init ();
+}
+
+#if defined (G_HAS_CONSTRUCTORS)
+# ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA
+#  pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(ide_init_ctor)
+# endif
+G_DEFINE_CONSTRUCTOR(ide_init_ctor)
+#else
+# error Your platform/compiler is missing constructor support
+#endif
diff --git a/libide/ide.h b/libide/ide.h
new file mode 100644
index 0000000..3cb1f40
--- /dev/null
+++ b/libide/ide.h
@@ -0,0 +1,78 @@
+/* ide.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_H
+#define IDE_H
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define IDE_INSIDE
+
+#include "ide-buffer.h"
+#include "ide-buffer-iter.h"
+#include "ide-build-result.h"
+#include "ide-build-system.h"
+#include "ide-builder.h"
+#include "ide-context.h"
+#include "ide-debugger.h"
+#include "ide-deployer.h"
+#include "ide-device.h"
+#include "ide-device-provider.h"
+#include "ide-device-manager.h"
+#include "ide-diagnostic.h"
+#include "ide-diagnostic-provider.h"
+#include "ide-executable.h"
+#include "ide-executer.h"
+#include "ide-file.h"
+#include "ide-global.h"
+#include "ide-indenter.h"
+#include "ide-language.h"
+#include "ide-object.h"
+#include "ide-process.h"
+#include "ide-project.h"
+#include "ide-project-file.h"
+#include "ide-project-files.h"
+#include "ide-project-item.h"
+#include "ide-refactory.h"
+#include "ide-script.h"
+#include "ide-search-engine.h"
+#include "ide-search-provider.h"
+#include "ide-search-result.h"
+#include "ide-service.h"
+#include "ide-symbol-resolver.h"
+#include "ide-symbol.h"
+#include "ide-target.h"
+#include "ide-test-case.h"
+#include "ide-test-suite.h"
+#include "ide-types.h"
+#include "ide-unsaved-files.h"
+#include "ide-vcs.h"
+
+#include "autotools/ide-autotools-build-system.h"
+#include "directory/ide-directory-build-system.h"
+#include "directory/ide-directory-vcs.h"
+#include "git/ide-git-vcs.h"
+#include "local/ide-local-device.h"
+
+#undef IDE_INSIDE
+
+G_END_DECLS
+
+#endif /* IDE_H */
diff --git a/libide/local/ide-local-device.c b/libide/local/ide-local-device.c
new file mode 100644
index 0000000..8464c18
--- /dev/null
+++ b/libide/local/ide-local-device.c
@@ -0,0 +1,117 @@
+/* ide-local-device.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/utsname.h>
+
+#include "ide-local-device.h"
+
+typedef struct
+{
+  GKeyFile *config;
+  gchar    *system_type;
+} IdeLocalDevicePrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeLocalDevice, ide_local_device, IDE_TYPE_DEVICE)
+
+static gchar *
+get_system_type (void)
+{
+  g_autoptr(gchar) os_lower = NULL;
+  struct utsname u;
+
+  if (uname (&u) < 0)
+    return g_strdup ("unknown");
+
+  os_lower = g_utf8_strdown (u.sysname, -1);
+
+  /*
+   * TODO: Clearly we want to discover "gnu", but that should be just fine
+   *       for a default until we try to actually run on something non-gnu.
+   *       Which seems unlikely at the moment. If you run FreeBSD, you can
+   *       probably fix this for me :-) And while you're at it, make the
+   *       uname() call more portable.
+   */
+
+#ifdef __GLIBC__
+  return g_strdup_printf ("%s-%s-%s", u.machine, os_lower, "gnu");
+#else
+# error Your platform is not yet supported.
+#endif
+}
+
+static const gchar *
+ide_local_device_get_system_type (IdeDevice *device)
+{
+  IdeLocalDevice *self = (IdeLocalDevice *)device;
+  IdeLocalDevicePrivate *priv = ide_local_device_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_LOCAL_DEVICE (device), NULL);
+  g_return_val_if_fail (IDE_IS_LOCAL_DEVICE (self), NULL);
+
+  return priv->system_type;
+}
+
+static GKeyFile *
+ide_local_device_get_config (IdeDevice *device)
+{
+  IdeLocalDevice *self = (IdeLocalDevice *)device;
+  IdeLocalDevicePrivate *priv = ide_local_device_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_LOCAL_DEVICE (device), NULL);
+  g_return_val_if_fail (IDE_IS_LOCAL_DEVICE (self), NULL);
+
+  return priv->config;
+}
+
+static void
+ide_local_device_finalize (GObject *object)
+{
+  IdeLocalDevice *self = (IdeLocalDevice *)object;
+  IdeLocalDevicePrivate *priv = ide_local_device_get_instance_private (self);
+
+  g_clear_pointer (&priv->config, g_key_file_unref);
+  g_clear_pointer (&priv->system_type, g_free);
+
+  G_OBJECT_CLASS (ide_local_device_parent_class)->finalize (object);
+}
+
+static void
+ide_local_device_class_init (IdeLocalDeviceClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeDeviceClass *device_class = IDE_DEVICE_CLASS (klass);
+
+  object_class->finalize = ide_local_device_finalize;
+
+  device_class->get_config = ide_local_device_get_config;
+  device_class->get_system_type = ide_local_device_get_system_type;
+}
+
+static void
+ide_local_device_init (IdeLocalDevice *self)
+{
+  IdeLocalDevicePrivate *priv = ide_local_device_get_instance_private (self);
+
+  priv->system_type = get_system_type ();
+  priv->config = g_key_file_new ();
+
+  g_key_file_set_string (priv->config, "autoconf", "--host", priv->system_type);
+
+  ide_device_set_display_name (IDE_DEVICE (self), g_get_host_name ());
+  ide_device_set_id (IDE_DEVICE (self), "local");
+}
diff --git a/libide/local/ide-local-device.h b/libide/local/ide-local-device.h
new file mode 100644
index 0000000..a36db2a
--- /dev/null
+++ b/libide/local/ide-local-device.h
@@ -0,0 +1,38 @@
+/* ide-local-device.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_LOCAL_DEVICE_H
+#define IDE_LOCAL_DEVICE_H
+
+#include "ide-device.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_LOCAL_DEVICE (ide_local_device_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeLocalDevice, ide_local_device, IDE, LOCAL_DEVICE,
+                          IdeDevice)
+
+struct _IdeLocalDeviceClass
+{
+  IdeDeviceClass parent;
+};
+
+G_END_DECLS
+
+#endif /* IDE_LOCAL_DEVICE_H */
diff --git a/libide/tasks/ide-load-directory-task.c b/libide/tasks/ide-load-directory-task.c
new file mode 100644
index 0000000..6a73f39
--- /dev/null
+++ b/libide/tasks/ide-load-directory-task.c
@@ -0,0 +1,295 @@
+/* ide-load-directory-task.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "ide-load-directory-task.h"
+#include "ide-project-item.h"
+#include "ide-project-file.h"
+
+#define NUM_FILES_DEFAULT 1000
+
+typedef struct
+{
+  gint        ref_count;
+  guint       failed : 1;
+  GTask      *task;
+  GHashTable *project_items;
+} TaskState;
+
+static void enumerate_children_cb (GObject      *object,
+                                   GAsyncResult *result,
+                                   gpointer      user_data);
+static void query_info_cb         (GObject      *object,
+                                   GAsyncResult *result,
+                                   gpointer      user_data);
+static void next_files_cb         (GObject      *object,
+                                   GAsyncResult *result,
+                                   gpointer      user_data);
+
+static void
+task_state_unref (TaskState *state)
+{
+  g_return_if_fail (state->ref_count > 0);
+
+  if (--state->ref_count == 0)
+    {
+      if (state->failed)
+        g_task_return_new_error (state->task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_FAILED,
+                                 _("Failed to enumerate files."));
+      else
+        g_task_return_boolean (state->task, TRUE);
+      g_hash_table_unref (state->project_items);
+      g_slice_free (TaskState, state);
+    }
+}
+
+static TaskState *
+task_state_ref (TaskState *state)
+{
+  g_return_if_fail (state->ref_count > 0);
+
+  ++state->ref_count;
+
+  return state;
+}
+
+static gboolean
+should_ignore_file (const gchar *name)
+{
+  g_return_val_if_fail (name, FALSE);
+
+  if (g_str_equal (name, ".git"))
+    return TRUE;
+
+  name = strrchr (name, '.');
+
+  if (name &&
+      (g_str_has_suffix (name, "~") ||
+       g_str_equal (name, ".so") ||
+       g_str_equal (name, ".o") ||
+       g_str_equal (name, ".la")))
+    return TRUE;
+
+  return FALSE;
+}
+
+static void
+next_files_cb (GObject      *object,
+               GAsyncResult *result,
+               gpointer      user_data)
+{
+  GFileEnumerator *enumerator = (GFileEnumerator *)object;
+  IdeProjectItem *parent;
+  IdeContext *context;
+  TaskState *state = user_data;
+  GError *error = NULL;
+  GFile *directory;
+  GList *list;
+  GList *iter;
+
+  g_return_if_fail (G_IS_FILE_ENUMERATOR (enumerator));
+  g_return_if_fail (state);
+
+  directory = g_file_enumerator_get_container (enumerator);
+  parent = g_hash_table_lookup (state->project_items, directory);
+  context = ide_object_get_context (IDE_OBJECT (parent));
+
+  list = g_file_enumerator_next_files_finish (enumerator, result, &error);
+
+  if (error)
+    {
+      state->failed = TRUE;
+      goto cleanup;
+    }
+
+  for (iter = list; iter; iter = iter->next)
+    {
+      IdeProjectItem *item;
+      GFileInfo *file_info = iter->data;
+      const gchar *name;
+      GFileType file_type;
+      GFile *file = NULL;
+
+      g_assert (G_IS_FILE_INFO (file_info));
+
+      file_type = g_file_info_get_file_type (file_info);
+
+      if ((file_type != G_FILE_TYPE_DIRECTORY) &&
+          (file_type != G_FILE_TYPE_REGULAR))
+        continue;
+
+      name = g_file_info_get_display_name (file_info);
+
+      if (should_ignore_file (name))
+        continue;
+
+      item = g_object_new (IDE_TYPE_PROJECT_FILE,
+                           "context", context,
+                           "file-info", file_info,
+                           "parent", parent,
+                           NULL);
+      ide_project_item_append (parent, item);
+
+      file = g_file_enumerator_get_child (enumerator, file_info);
+
+      if (file_type == G_FILE_TYPE_DIRECTORY)
+        {
+          g_hash_table_insert (state->project_items,
+                               g_object_ref (file),
+                               g_object_ref (item));
+          g_file_enumerate_children_async (file,
+                                           (G_FILE_ATTRIBUTE_STANDARD_TYPE","
+                                            G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME),
+                                           G_FILE_QUERY_INFO_NONE,
+                                           G_PRIORITY_DEFAULT,
+                                           g_task_get_cancellable (state->task),
+                                           enumerate_children_cb,
+                                           task_state_ref (state));
+        }
+
+      g_clear_object (&item);
+      g_clear_object (&file);
+    }
+
+cleanup:
+  g_list_foreach (list, (GFunc)g_object_unref, NULL);
+  g_list_free (list);
+  task_state_unref (state);
+}
+
+static void
+enumerate_children_cb (GObject      *object,
+                       GAsyncResult *result,
+                       gpointer      user_data)
+{
+  GFileEnumerator *enumerator;
+  TaskState *state = user_data;
+  GError *error = NULL;
+  GFile *directory = (GFile *)object;
+
+  g_return_if_fail (G_IS_FILE (directory));
+  g_return_if_fail (state);
+
+  enumerator = g_file_enumerate_children_finish (directory, result, &error);
+
+  if (!enumerator)
+    {
+      state->failed = TRUE;
+      goto cleanup;
+    }
+
+  g_file_enumerator_next_files_async (enumerator,
+                                      NUM_FILES_DEFAULT,
+                                      G_PRIORITY_DEFAULT,
+                                      g_task_get_cancellable (state->task),
+                                      next_files_cb,
+                                      task_state_ref (state));
+
+cleanup:
+  g_clear_object (&enumerator);
+  task_state_unref (state);
+}
+
+static void
+query_info_cb (GObject      *object,
+               GAsyncResult *result,
+               gpointer      user_data)
+{
+  GFileInfo *file_info;
+  GFileType file_type;
+  TaskState *state = user_data;
+  GError *error = NULL;
+  GFile *directory = (GFile *)object;
+
+  g_return_if_fail (G_IS_FILE (directory));
+  g_return_if_fail (state);
+
+  file_info = g_file_query_info_finish (directory, result, &error);
+
+  if (!file_info)
+    {
+      state->failed = TRUE;
+      goto cleanup;
+    }
+
+  file_type = g_file_info_get_file_type (file_info);
+
+  if (file_type != G_FILE_TYPE_DIRECTORY)
+    {
+      state->failed = TRUE;
+      goto cleanup;
+    }
+
+  g_file_enumerate_children_async (directory,
+                                   (G_FILE_ATTRIBUTE_STANDARD_TYPE","
+                                    G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME),
+                                   G_FILE_QUERY_INFO_NONE,
+                                   G_PRIORITY_DEFAULT,
+                                   g_task_get_cancellable (state->task),
+                                   enumerate_children_cb,
+                                   task_state_ref (state));
+
+cleanup:
+  g_clear_object (&file_info);
+  task_state_unref (state);
+}
+
+GTask *
+ide_load_directory_task_new (gpointer             source_object,
+                             GFile               *directory,
+                             IdeProjectItem      *parent,
+                             int                  io_priority,
+                             GCancellable        *cancellable,
+                             GAsyncReadyCallback  callback,
+                             gpointer             user_data)
+{
+  TaskState *state;
+  GTask *task;
+
+  g_return_val_if_fail (G_IS_FILE (directory), NULL);
+  g_return_val_if_fail (IDE_IS_PROJECT_ITEM (parent), NULL);
+  g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL);
+
+  state = g_slice_new0 (TaskState);
+  state->ref_count = 1;
+  state->task = g_task_new (source_object, cancellable, callback, user_data);
+  state->project_items = g_hash_table_new_full (g_direct_hash,
+                                                g_direct_equal,
+                                                g_object_unref,
+                                                g_object_unref);
+
+  g_hash_table_insert (state->project_items,
+                       g_object_ref (directory),
+                       g_object_ref (parent));
+
+  g_file_query_info_async (directory,
+                           G_FILE_ATTRIBUTE_STANDARD_TYPE,
+                           G_FILE_QUERY_INFO_NONE,
+                           io_priority,
+                           cancellable,
+                           query_info_cb,
+                           task_state_ref (state));
+
+  task = g_object_ref (state->task);
+  task_state_unref (state);
+
+  return task;
+}
diff --git a/libide/tasks/ide-load-directory-task.h b/libide/tasks/ide-load-directory-task.h
new file mode 100644
index 0000000..c879b63
--- /dev/null
+++ b/libide/tasks/ide-load-directory-task.h
@@ -0,0 +1,38 @@
+/* ide-load-directory-task.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_LOAD_DIRECTORY_TASK_H
+#define IDE_LOAD_DIRECTORY_TASK_H
+
+#include <gio/gio.h>
+
+#include "ide-types.h"
+
+G_BEGIN_DECLS
+
+GTask *ide_load_directory_task_new (gpointer             source_object,
+                                    GFile               *directory,
+                                    IdeProjectItem      *parent,
+                                    int                  io_priority,
+                                    GCancellable        *cancellable,
+                                    GAsyncReadyCallback  callback,
+                                    gpointer             user_data);
+
+G_END_DECLS
+
+#endif /* IDE_LOAD_DIRECTORY_TASK_H */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]