diff --git a/build/configure.jam b/build/configure.jam
index 66b81b3..9b26e04 100644
--- a/build/configure.jam
+++ b/build/configure.jam
@@ -123,76 +123,87 @@ rule print-configure-checks-summary ( )
     }
 }
 
-
-# Attempt to build a metatarget named by 'metatarget-reference' in context of
-# 'project' with properties 'ps'. Returns non-empty value if build is OK.
-#
-rule builds-raw ( metatarget-reference : project : ps : what : retry ? )
+# Attempts to build a set of virtual targets
+rule try-build ( targets * : ps : what : retry ? )
 {
+   local cache-name = $(what) [ $(ps).raw ] ;
+   cache-name = $(cache-name:J=-) ;
+   local value = [ config-cache.get $(cache-name) ] ;
+        
     local result ;
+    local jam-targets ;
 
-    if ! $(retry) && ! $(.$(what)-tested.$(ps))
+    for local t in $(targets[2-])
     {
-        .$(what)-tested.$(ps) = true ;
-
-        local cache-name = $(what) [ $(ps).raw ] ;
-        cache-name = $(cache-name:J=-) ;
-        local value = [ config-cache.get $(cache-name) ] ;
-        
-        local targets = [ targets.generate-from-reference 
-            $(metatarget-reference) : $(project) : $(ps) ] ;
-
-        local jam-targets ;
-        for local t in $(targets[2-])
+        jam-targets += [ $(t).actualize ] ;
+    }
+         
+    if $(value)
+    {
+        local x = [ PAD "    - $(what)" : $(.width) ] ; 
+        if $(value) = true
         {
-            jam-targets += [ $(t).actualize ] ;
+            .$(what)-supported.$(ps) = yes ;
+            result = true ;
+            log-check-result "$(x) : yes (cached)" ;
         }
-             
-        if $(value)
+        else
         {
-            local x = [ PAD "    - $(what)" : $(.width) ] ; 
-            if $(value) = true
-            {
-                .$(what)-supported.$(ps) = yes ;
-                result = true ;
-                log-check-result "$(x) : yes (cached)" ;
-            }
-            else
-            {
-                log-check-result "$(x) : no  (cached)" ;
-            }
-        }       
-        else if ! UPDATE_NOW in [ RULENAMES ]
+            log-check-result "$(x) : no  (cached)" ;
+        }
+    }       
+    else if ! UPDATE_NOW in [ RULENAMES ]
+    {
+        # Cannot determine. Assume existance.
+    }
+    else
+    {
+        local x = [ PAD "    - $(what)" : $(.width) ] ;
+        if [ UPDATE_NOW $(jam-targets) :
+             $(.log-fd) : ignore-minus-n : ignore-minus-q ]
         {
-            # Cannot determine. Assume existance.
+            .$(what)-supported.$(ps) = yes ;
+            result = true ;
+            log-check-result "$(x) : yes" ;
         }
         else
         {
-            local x = [ PAD "    - $(what)" : $(.width) ] ;
-            if [ UPDATE_NOW $(jam-targets) :
-                 $(.log-fd) : ignore-minus-n : ignore-minus-q ]
-            {
-                .$(what)-supported.$(ps) = yes ;
-                result = true ;
-                log-check-result "$(x) : yes" ;
-            }
-            else
-            {
-                log-check-result "$(x) : no" ;
-            }
+            log-check-result "$(x) : no" ;
+        }
+    }
+    if ! $(value)
+    {
+        if $(result)
+        {
+            config-cache.set $(cache-name) : true ;
         }
-        if ! $(value)
+        else
         {
-            if $(result)
-            {
-                config-cache.set $(cache-name) : true ;
-            }
-            else
-            {
-                config-cache.set $(cache-name) : false ;
-            }
-        }        
+            config-cache.set $(cache-name) : false ;
+        }
+    }        
+    return $(result) ;
+}
+
+# Attempt to build a metatarget named by 'metatarget-reference'
+# in context of 'project' with properties 'ps'.
+# Returns non-empty value if build is OK.
+rule builds-raw ( metatarget-reference : project : ps : what : retry ? )
+{
+    local result ;
+
+    if ! $(retry) && ! $(.$(what)-tested.$(ps))
+    {
+        .$(what)-tested.$(ps) = true ;
+
+        local targets = [ targets.generate-from-reference 
+            $(metatarget-reference) : $(project) : $(ps) ] ;
+
+        result = [ try-build $(targets[2-]) : $(ps) : $(what) : $(retry) ] ;
+        .$(what)-supported.$(ps) = $(result) ;
+
         return $(result) ;
+
     }
     else
     {
diff --git a/build/virtual-target.jam b/build/virtual-target.jam
index f62eadb..44a706d 100644
--- a/build/virtual-target.jam
+++ b/build/virtual-target.jam
@@ -1324,8 +1324,9 @@ class subvariant
         for local t in $(self.created-targets)
         {
             # Skip targets of the wrong type.
+            local type = [ $(t).type ] ;
             if ! $(target-type) ||
-                [ type.is-derived [ $(t).type ] $(target-type) ]
+                ( $(type) && [ type.is-derived $(type) $(target-type) ] )
             {
                 result = [ sequence.merge $(result) : [ $(t).path ] ] ;
             }
diff --git a/engine/builtins.c b/engine/builtins.c
index e4130bb..28ff753 100644
--- a/engine/builtins.c
+++ b/engine/builtins.c
@@ -29,6 +29,10 @@
 
 #include <ctype.h>
 
+#ifdef NT
+#include <windows.h>
+#endif
+
 #if defined(USE_EXECUNIX)
 # include <sys/types.h>
 # include <sys/wait.h>
@@ -426,6 +430,11 @@ void load_builtins()
         bind_builtin( "MAKEDIR", builtin_makedir, 0, args );
     }
 
+    {
+        const char * args [] = { "path", 0 };
+        bind_builtin( "READLINK", builtin_readlink, 0, args );
+    }
+
     /* Initialize builtin modules. */
     init_set();
     init_path();
@@ -1828,6 +1837,94 @@ LIST * builtin_makedir( FRAME * frame, int flags )
 }
 
 
+LIST *builtin_readlink( FRAME * frame, int flags )
+{
+    const char * path = object_str( list_front( lol_get( frame->args, 0 ) ) );
+#ifdef NT
+
+    /* This struct is declared in ntifs.h which is
+     * part of the Windows Driver Kit.
+     */
+    typedef struct _REPARSE_DATA_BUFFER {
+        ULONG ReparseTag;
+        USHORT ReparseDataLength;
+        USHORT Reserved;
+        union {
+            struct {
+                USHORT SubstituteNameOffset;
+                USHORT SubstituteNameLength;
+                USHORT PrintNameOffset;
+                USHORT PrintNameLength;
+                ULONG Flags;
+                WCHAR PathBuffer[ 1 ];
+            } SymbolicLinkReparseBuffer;
+            struct {
+                USHORT SubstituteNameOffset;
+                USHORT SubstituteNameLength;
+                USHORT PrintNameOffset;
+                USHORT PrintNameLength;
+                WCHAR PathBuffer[ 1 ];
+            } MountPointReparseBuffer;
+            struct {
+                UCHAR DataBuffer[ 1 ];
+            } GenericReparseBuffer;
+        };
+    } REPARSE_DATA_BUFFER;
+
+    HANDLE hLink = CreateFileA( path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL );
+    DWORD n;
+    union {
+        REPARSE_DATA_BUFFER reparse;
+        char data[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+    } buf;
+    int okay = DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, &buf, sizeof(buf), &n, NULL);
+
+    CloseHandle( hLink );
+
+    if (okay && buf.reparse.ReparseTag == IO_REPARSE_TAG_SYMLINK )
+    {
+        int index = buf.reparse.SymbolicLinkReparseBuffer.SubstituteNameOffset / 2;
+        int length = buf.reparse.SymbolicLinkReparseBuffer.SubstituteNameLength / 2;
+        char cbuf[MAX_PATH + 1];
+        int numchars = WideCharToMultiByte( CP_ACP, 0, buf.reparse.SymbolicLinkReparseBuffer.PathBuffer + index, length, cbuf, sizeof(cbuf), NULL, NULL );
+        if( numchars >= sizeof(cbuf) )
+        {
+            return 0;
+        }
+        cbuf[numchars] = '\0';
+        return list_new( object_new( cbuf ) );
+    }
+    return 0;
+#else
+    char static_buf[256];
+    char * buf = static_buf;
+    size_t bufsize = 256;
+    LIST * result = 0;
+    while (1) {
+        ssize_t len = readlink( path, buf, bufsize );
+        if ( len < 0 )
+        {
+            break;
+        }
+        else if ( len < bufsize )
+        {
+            buf[ len ] = '\0';
+            result = list_new( object_new( buf ) );
+            break;
+        }
+        if ( buf != static_buf )
+            BJAM_FREE( buf );
+        bufsize *= 2;
+        buf = BJAM_MALLOC( bufsize );
+    }
+    
+    if ( buf != static_buf )
+        BJAM_FREE( buf );
+
+    return result;
+#endif
+}
+
 #ifdef HAVE_PYTHON
 
 LIST * builtin_python_import_rule( FRAME * frame, int flags )
diff --git a/engine/builtins.h b/engine/builtins.h
index b7a967c..6d0c873 100644
--- a/engine/builtins.h
+++ b/engine/builtins.h
@@ -63,6 +63,7 @@ LIST *builtin_pad( FRAME * frame, int flags );
 LIST *builtin_precious( FRAME * frame, int flags );
 LIST *builtin_self_path( FRAME * frame, int flags );
 LIST *builtin_makedir( FRAME * frame, int flags );
+LIST *builtin_readlink( FRAME * frame, int flags );
 
 void backtrace( FRAME *frame );
 extern int last_update_now_status;
diff --git a/tools/link.jam b/tools/link.jam
new file mode 100644
index 0000000..06e11e5
--- /dev/null
+++ b/tools/link.jam
@@ -0,0 +1,434 @@
+# Copyright 2012 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+
+import os ;
+import targets ;
+import project ;
+import "class" : new ;
+import virtual-target ;
+import configure ;
+import path ;
+import property ;
+import property-set ;
+import common ;
+
+rule get-root-project ( project )
+{
+    # Find the root project.
+    local root-project = $(project) ;
+    root-project = [ $(root-project).project-module ] ;
+    while
+        [ project.attribute $(root-project) parent-module ] &&
+        [ project.attribute $(root-project) parent-module ] != user-config &&
+        [ project.attribute $(root-project) parent-module ] != project-config
+    {
+        root-project = [ project.attribute $(root-project) parent-module ] ;
+    }
+    return $(root-project) ;
+}
+
+TOUCH = [ common.file-touch-command ] ;
+
+actions touch {
+   $(TOUCH) "$(<)"
+}
+
+rule can-symlink ( project )
+{
+    if ! $(.can-symlink)
+    {
+        local root-project = [ get-root-project $(project) ] ;
+
+        local source-target = [ new file-target test-symlink-source : :
+            $(project) : [ new action : link.touch ] ] ;
+        local target = [ new file-target test-symlink : :
+            $(project) : [ new action $(source-target) : link.mklink ] ] ;
+
+        local ps = [ property-set.create $(relevant) ] ;
+
+        if [ configure.try-build $(target) : $(ps) : "symlinks supported" ]
+        {
+            .can-symlink = true ;
+        }
+        else
+        {
+            .can-symlink = false ;
+        }
+    }
+    if $(.can-symlink) = true
+    {
+        return true ;
+    }
+}
+
+
+rule can-hardlink ( project )
+{
+    if ! $(.can-hardlink)
+    {
+        # Find the root project.
+        local root-project = [ get-root-project $(project) ] ;
+
+        local source-target = [ new file-target test-hardlink-source : :
+            $(project) : [ new action : link.touch ] ] ;
+        local target = [ new file-target test-hardlink : :
+            $(project) : [ new action $(source-target) : link.hardlink ] ] ;
+
+        if [ configure.try-build $(target) : "hardlinks supported" ]
+        {
+            .can-hardlink = true ;
+        }
+        else
+        {
+            .can-hardlink = false ;
+        }
+    }
+    if $(.can-hardlink) = true
+    {
+        return true ;
+    }
+}
+
+class file-or-directory-reference : basic-target
+{
+    import virtual-target ;
+    import property-set ;
+    import path ;
+
+    rule construct ( name : source-targets * : property-set )
+    {
+        return [ property-set.empty ] [ virtual-target.from-file $(self.name) :
+            [ location ] : $(self.project) ] ;
+    }
+
+    # Returns true if the referred file really exists.
+    rule exists ( )
+    {
+        location ;
+        return $(self.file-path) ;
+    }
+
+    # Returns the location of target. Needed by 'testing.jam'.
+    rule location ( )
+    {
+        if ! $(self.file-location)
+        {
+            local source-location = [ $(self.project).get source-location ] ;
+            for local src-dir in $(source-location)
+            {
+                if ! $(self.file-location)
+                {
+                    local location = [ path.root $(self.name) $(src-dir) ] ;
+                    if [ path.exists [ path.native $(location) ] ]
+                    {
+                         self.file-location = $(src-dir) ;
+                         self.file-path = $(location) ;
+                    }
+                }
+            }
+        }
+        return $(self.file-location) ;
+    }
+}
+
+class symlink-target-class : basic-target
+{
+    import path ;
+    import virtual-target ;
+    import link ;
+    import os ;
+    import type ;
+    rule construct ( name : source-target : property-set )
+    {
+        local location = [ path.join
+            [ $(source-target).path ] [ $(source-target).name ] ] ;
+        local files = [ path.glob-tree $(location) : * ] ;
+        local targets ;
+
+        if ! [ link.can-symlink $(self.project) ]
+        {
+            link.can-hardlink $(self.project) ;
+        }
+
+        if [ $(property-set).get <location> ]
+        {
+            property-set = [ property-set.create
+                [ property.select <location> : [ $(property-set).raw ] ] ] ;
+        }
+        else
+        {
+            local path,relative-to-build-dir = [ $(property-set).target-path ] ;
+            local path = $(path,relative-to-build-dir[1]) ;
+            local relative-to-build-dir = $(path,relative-to-build-dir[2]) ;
+
+            if $(relative-to-build-dir)
+            {
+                path = [ path.join [ $(self.project).build-dir ] $(path) ] ;
+            }
+
+            property-set = [ property-set.create <location>$(path) ] ;
+        }
+
+        local a = [ new non-scanning-action $(source-target) :
+            link.do-link-recursively : $(property-set) ] ;
+
+        local t = [ new notfile-target $(name)
+            : $(self.project) : $(a) ] ;
+
+        return [ property-set.empty ] [ virtual-target.register $(t) ] ;
+    }
+}
+
+rule do-file-link
+{
+    local target = [ path.native [ path.relative-to [ path.pwd ] $(<) ] ] ;
+    local source = [ path.native [ path.relative-to [ path.pwd ] $(>) ] ] ;
+    LOCATE on $(target) = . ;
+    DEPENDS $(.current-target) : $(target) ;
+    if $(.can-symlink) = true
+    {
+        link.mklink $(target) : $(source) ;
+    }
+    else if $(.can-hardlink) = true
+    {
+        link.hardlink $(target) : $(source) ;
+    }
+    else
+    {
+        common.copy $(target) : $(source) ;
+    }
+}
+
+rule do-link
+{
+    local target = [ path.native [ path.relative-to [ path.pwd ] $(<) ] ] ;
+    local source = [ path.native [ path.relative-to [ path.pwd ] $(>) ] ] ;
+    local relative = [ path.native [ path.relative-to [ path.parent $(<) ] $(>) ] ] ;
+    if ! [ on $(target) return $(MKLINK_OR_DIR) ]
+    {
+        LOCATE on $(target) = . ;
+        DEPENDS $(.current-target) : $(target) ;
+        mklink-or-dir $(target) : $(source) ;
+    }
+    if [ os.name ] = NT
+    {
+        MKLINK_OR_DIR on $(target) = mklink /D \"$(target)\" \"$(relative)\" ;
+    }
+    else
+    {
+        MKLINK_OR_DIR on $(target) = ln -s $(relative) $(target)  ;
+    }
+}
+
+rule do-split
+{
+    local target = [ path.native [ path.relative-to [ path.pwd ] $(<) ] ] ;
+    if ! [ on $(target) return $(MKLINK_OR_DIR) ]
+    {
+        LOCATE on $(target) = . ;
+        DEPENDS $(.current-target) : $(target) ;
+        common.mkdir $(target) ;
+    }
+    if ! [ on $(target) return $(RM) ]
+    {
+        NOUPDATE $(target) ;
+    }
+    MKLINK_OR_DIR on $(target) = mkdir \"$(target)\" ;
+}
+
+rule do-rm
+{
+    local target = [ path.native [ path.relative-to [ path.pwd ] $(<) ] ] ;
+    ALWAYS $(target) ;
+    RM on $(target) = rmdir ;
+    link.rm $(target) ;
+}
+
+actions mklink-or-dir
+{
+    $(MKLINK_OR_DIR)
+}
+
+rule link-entries ( target : files * : split ? )
+{
+    for local s in $(files)
+    {
+        local t = [ path.join $(target) [ path.basename $(s) ] ] ;
+        if ! $(.known-dirs.$(t))
+        {
+            local t = [ path.native [ path.relative-to [ path.pwd ] $(t) ] ] ;
+            local s = [ path.native [ path.relative-to [ path.pwd ] $(target) ] ] ;
+            LOCATE on $(t) = . ;
+            DEPENDS $(t) : $(s) ;
+        }
+        if $(split)
+        {
+            link-recursively $(t) : $(s) ;
+        }
+        else
+        {
+            link-entries $(t) : [ path.glob $(s) : * ] ;
+        }
+    }
+    if ! $(.known-dirs.$(target))
+    {
+        .known-dirs.$(target) += $(files) ;
+        .known-dirs.base.$(target) = $(.current-target) ;
+    }
+}
+
+rule link-recursively ( target : source : no-recurse ? )
+{
+    local split ;
+    if [ CHECK_IF_FILE [ path.native $(source) ] ]
+    {
+        do-file-link $(target) : $(source) ;
+    }
+    else if $(.known-dirs.$(target)) && ! $(no-recurse)
+    {
+        split = true ;
+        if ! $(.split-dirs.$(target))
+        {
+            local .current-target = $(.known-dirs.base.$(target)) ;
+            for local s in $(.known-dirs.$(target))
+            {
+                local t = [ path.join $(target) [ path.basename $(s) ] ] ;
+                link-recursively $(t) : $(s) : flat ;
+            }
+            if [ READLINK [ path.native $(target) ] ]
+            {
+                do-rm $(target) ;
+            }
+            do-split $(target) ;
+            .split-dirs.$(target) = true ;
+        }
+    }
+    else if [ path.exists [ path.native $(target) ] ]
+    {
+        local link-target = [ READLINK [ path.native $(target) ] ] ;
+        if $(link-target)
+        {
+            local full-path =
+                [ path.root [ path.make $(link-target) ] [ path.parent $(target) ] ] ;
+            if $(full-path) != $(source)
+            {
+                do-rm $(target) ;
+                do-split $(target) ;
+                split = true ;
+            }
+        }
+        else
+        {
+            do-split $(target) ;
+            split = true ;
+        }
+    }
+    else if $(.can-symlink) = false
+    {
+        if [ READLINK [ path.native $(target) ] ]
+        {
+            do-rm $(target) ;
+        }
+        do-split $(target) ;
+        split = true ;
+    }
+    else
+    {
+        do-link $(target) : $(source) ;
+    }
+
+    if ! $(no-recurse)
+    {
+        link-entries $(target) : [ path.glob $(source) : * ] : $(split) ;
+    }
+}
+
+rule do-link-recursively ( target : source : properties * )
+{
+    local target-path = [ property.select <location> : $(properties) ] ;
+    local source-path = [ on $(source) return $(LOCATE) ] [ on $(source) return $(SEARCH) ] ;
+
+    local absolute-target = [ path.root
+        [ path.join [ path.make $(target-path[1]:G=) ]
+                    [ path.basename [ path.make $(source:G=) ] ] ]
+        [ path.pwd ] ] ;
+
+    local absolute-source = [ path.root
+        [ path.root [ path.make $(source:G=) ]
+                    [ path.make $(source-path[1]) ] ]
+        [ path.pwd ] ] ;
+
+    local .current-target = $(target) ;
+
+    link-recursively $(absolute-target) : $(absolute-source) ;
+}
+
+rule mklink
+{
+    local target-path = [ on $(<) return $(LOCATE) ] [ on $(<) return $(SEARCH) ] . ;
+    local source-path = [ on $(>) return $(LOCATE) ] [ on $(>) return $(SEARCH) ] . ;
+    local relative-path = [ path.relative-to
+        [ path.parent [ path.join [ path.root [ path.make $(target-path[1]) ] [ path.pwd ] ] [ path.make $(<:G=) ] ] ]
+        [ path.join [ path.root [ path.make $(source-path[1]) ] [ path.pwd ] ] [ path.make $(>:G=) ] ] ] ;
+
+    PATH_TO_SOURCE on $(<) = [ path.native $(relative-path) ] ;
+    NOUPDATE $(<) ;
+}
+
+if [ os.name ] = NT
+{
+
+actions mklink
+{
+    if exist "$(<)" del "$(<)"
+    mklink "$(<)" "$(PATH_TO_SOURCE)"
+}
+
+actions hardlink
+{
+    if exist "$(<)" del "$(<)"
+    mklink /H "$(<)" "$(>)"
+}
+
+actions rm
+{
+    rmdir "$(<)"
+}
+
+}
+else
+{
+
+actions mklink
+{
+    ln -f -s "$(PATH_TO_SOURCE)" "$(<)"
+}
+
+actions hardlink
+{
+    ln -f "$(>)" "$(<)"
+}
+
+actions rm
+{
+    rmdir "$(<)"
+}
+
+}
+
+rule link-directory ( name : sources : requirements * : default-build * : usage-requirements * )
+{
+    local project = [ project.current ] ;
+    sources = [ new file-or-directory-reference $(sources) : $(project) ] ;
+    targets.main-target-alternative $(sources) ;
+    return [ targets.main-target-alternative
+        [ new symlink-target-class $(name) : $(project)
+            : [ targets.main-target-sources $(sources) : $(name) : no-renaming ]
+            : [ targets.main-target-requirements $(requirements) : $(project) ]
+            : [ targets.main-target-default-build : $(project) ]
+            : [ targets.main-target-usage-requirements $(usage-requirements) :
+                $(project) ] ] ] ;
+}
+
+IMPORT $(__name__) : link-directory : : link-directory ;
