Index: trunk/src/kmk/kbuild-read.c
===================================================================
--- trunk/src/kmk/kbuild-read.c	(revision 2717)
+++ trunk/src/kmk/kbuild-read.c	(revision 2718)
@@ -284,16 +284,15 @@
         && g_fKbObjCompMode)
     {
-        size_t  cchPrefixed = pObj->cchVarPrefix + cchName;
-        char   *pszPrefixed = xmalloc(cchPrefixed + 1);
+        struct variable *pAlias;
+        size_t           cchPrefixed = pObj->cchVarPrefix + cchName;
+        char            *pszPrefixed = xmalloc(cchPrefixed + 1);
         memcpy(pszPrefixed, pObj->pszVarPrefix, pObj->cchVarPrefix);
         memcpy(&pszPrefixed[pObj->cchVarPrefix], pszName, cchName);
         pszPrefixed[cchPrefixed] = '\0';
 
-        /** @todo implement variable aliases or something. */
-        define_variable_in_set(pszPrefixed, cchPrefixed,
-                               pchValue, cchValue, 1 /*duplicate_value*/,
-                               enmOrigin, fRecursive,
-                               &global_variable_set,
-                               pFileLoc);
+        pAlias = define_variable_alias_in_set(pszPrefixed, cchPrefixed, pVar, enmOrigin,
+                                              &global_variable_set, pFileLoc);
+        if (!pAlias->alias)
+            error(pFileLoc, _("Error defining alias '%s'"), pszPrefixed);
     }
 
Index: trunk/src/kmk/testcase-kBuild-define.kmk
===================================================================
--- trunk/src/kmk/testcase-kBuild-define.kmk	(revision 2717)
+++ trunk/src/kmk/testcase-kBuild-define.kmk	(revision 2718)
@@ -92,5 +92,5 @@
 [target@OutsideMod]_OTHER   = outside-value
 $(if-expr "$([target@OutsideMod]_SOURCES)" == "file2.c file3.c file4.c",,$(error [target@OutsideMod]_SOURCES is '$([target@OutsideMod]_SOURCES)' not 'file2.c file3.c file4.c'))
-$(if-expr "OutsideMod_SOURCES"             == "file2.c file3.c file4.c",,$(error OutsideMod_SOURCES is '$(OutsideMod_SOURCES)' not 'file2.c file3.c file4.c'))
+$(if-expr "$(OutsideMod_SOURCES)"          == "file2.c file3.c file4.c",,$(error OutsideMod_SOURCES is '$(OutsideMod_SOURCES)' not 'file2.c file3.c file4.c'))
 
 $(if-expr "$([target@OutsideMod]_OTHER)" == "outside-value",,$(error [target@OutsideMod]_OTHER is '$([target@OutsideMod]_OTHER)' not 'outside-value'))
Index: trunk/src/kmk/variable.c
===================================================================
--- trunk/src/kmk/variable.c	(revision 2717)
+++ trunk/src/kmk/variable.c	(revision 2718)
@@ -42,4 +42,18 @@
 # include <stddef.h>
 #endif
+
+#ifdef KMK
+/** Gets the real variable if alias.  For use when looking up variables. */
+# define RESOLVE_ALIAS_VARIABLE(v) \
+  do { \
+    if ((v) != NULL && (v)->alias) \
+      { \
+        (v) = (struct variable *)(v)->value; \
+        assert ((v)->aliased); \
+        assert (!(v)->alias); \
+      } \
+  } while (0)
+#endif
+
 
 /* Chain of all pattern-specific variables.  */
@@ -308,4 +322,7 @@
   if (! HASH_VACANT (v))
     {
+#ifdef KMK
+      RESOLVE_ALIAS_VARIABLE(v);
+#endif
       if (env_overrides && v->origin == o_env)
 	/* V came from in the environment.  Since it was defined
@@ -423,4 +440,8 @@
   v->append = 0;
   v->private_var = 0;
+#ifdef KMK
+  v->alias = 0;
+  v->aliased = 0;
+#endif
   v->export = v_default;
   MAKE_STATS_2(v->changes = 0);
@@ -489,4 +510,15 @@
   if (! HASH_VACANT (v))
     {
+#ifdef KMK
+      if (v->aliased || v->alias)
+        {
+           if (v->aliased)
+             error (NULL, _("Cannot undefine the aliased variable '%s'"), v->name);
+           else
+             error (NULL, _("Cannot undefine the variable alias '%s'"), v->name);
+          return;
+        }
+#endif
+
       if (env_overrides && v->origin == o_env)
 	/* V came from in the environment.  Since it was defined
@@ -507,4 +539,108 @@
     }
 }
+
+#ifdef KMK
+/* Define variable named NAME as an alias of the variable TARGET.
+   SET defaults to the global set if NULL. FLOCP is just for completeness. */
+
+struct variable *
+define_variable_alias_in_set (const char *name, unsigned int length,
+                              struct variable *target, enum variable_origin origin,
+                              struct variable_set *set, const struct floc *flocp)
+{
+  struct variable     *v;
+  struct variable     **var_slot;
+
+  /* Look it up the hash table slot for it. */
+  name = strcache2_add (&variable_strcache, name, length);
+  if (   set != &global_variable_set
+      || !(v = strcache2_get_user_val (&variable_strcache, name)))
+    {
+      struct variable var_key;
+
+      var_key.name = name;
+      var_key.length = length;
+      var_slot = (struct variable **) hash_find_slot_strcached (&set->table, &var_key);
+      v = *var_slot;
+    }
+  else
+    {
+      assert (!v || (v->name == name && !HASH_VACANT (v)));
+      var_slot = 0;
+    }
+  if (! HASH_VACANT (v))
+    {
+      /* A variable of this name is already defined.
+         If the old definition is from a stronger source
+         than this one, don't redefine it.  */
+
+      if (env_overrides && v->origin == o_env)
+        /* V came from in the environment.  Since it was defined
+           before the switches were parsed, it wasn't affected by -e.  */
+        v->origin = o_env_override;
+
+      if ((int) origin < (int) v->origin)
+        return v;
+
+      if (v->value != 0 && !v->rdonly_val)
+          free (v->value);
+      MAKE_STATS_2(v->changes++);
+    }
+  else
+    {
+      /* Create a new variable definition and add it to the hash table.  */
+      v = alloccache_alloc (&variable_cache);
+      v->name = name; /* already cached. */
+      v->length = length;
+      hash_insert_at (&set->table, v, var_slot);
+      v->special = 0;
+      v->expanding = 0;
+      v->exp_count = 0;
+      v->per_target = 0;
+      v->append = 0;
+      v->private_var = 0;
+      v->aliased = 0;
+      v->export = v_default;
+      MAKE_STATS_2(v->changes = 0);
+      MAKE_STATS_2(v->reallocs = 0);
+      v->exportable = 1;
+      if (*name != '_' && (*name < 'A' || *name > 'Z')
+          && (*name < 'a' || *name > 'z'))
+        v->exportable = 0;
+      else
+        {
+          for (++name; *name != '\0'; ++name)
+            if (*name != '_' && (*name < 'a' || *name > 'z')
+                && (*name < 'A' || *name > 'Z') && !ISDIGIT(*name))
+              break;
+
+          if (*name != '\0')
+            v->exportable = 0;
+        }
+
+     /* If it's the global set, remember the variable. */
+     if (set == &global_variable_set)
+       strcache2_set_user_val (&variable_strcache, v->name, v);
+    }
+
+  /* Common variable setup. */
+  v->alias = 1;
+  v->rdonly_val = 1;
+  v->value = (char *)target;
+  v->value_length = sizeof(*target); /* Non-zero to provoke trouble. */
+  v->value_alloc_len = sizeof(*target);
+  if (flocp != 0)
+    v->fileinfo = *flocp;
+  else
+    v->fileinfo.filenm = 0;
+  v->origin = origin;
+  v->recursive = 0;
+
+  /* Mark the target as aliased. */
+  target->aliased = 1;
+
+  return v;
+}
+#endif /* KMK */
 
 /* If the variable passed in is "special", handle its special nature.
@@ -785,5 +921,10 @@
 # endif /* CONFIG_WITH_STRCACHE2 */
       if (v && (!is_parent || !v->private_var))
-	return v->special ? lookup_special_var (v) : v;
+        {
+# ifdef KMK
+          RESOLVE_ALIAS_VARIABLE(v);
+# endif
+	  return v->special ? lookup_special_var (v) : v;
+        }
 
       is_parent |= setlist->next_is_parent;
@@ -869,4 +1010,7 @@
 {
   struct variable var_key;
+#ifdef KMK
+  struct variable *v;
+#endif
 #ifndef CONFIG_WITH_STRCACHE2
   var_key.name = (char *) name;
@@ -883,5 +1027,8 @@
       struct variable *v = lookup_kbuild_object_variable_accessor(name, length);
       if (v != VAR_NOT_KBUILD_ACCESSOR)
-        return v;
+        {
+          RESOLVE_ALIAS_VARIABLE(v);
+          return v;
+        }
     }
 # endif
@@ -895,15 +1042,19 @@
   if (set == &global_variable_set)
     {
-      struct variable *v;
       v = strcache2_get_user_val (&variable_strcache, cached_name);
       assert (!v || v->name == cached_name);
-      return v;
-    }
-
-  var_key.name = cached_name;
-  var_key.length = length;
-
-  return (struct variable *) hash_find_item_strcached (
-    (struct hash_table *) &set->table, &var_key);
+    }
+  else
+    {
+      var_key.name = cached_name;
+      var_key.length = length;
+
+      v = (struct variable *) hash_find_item_strcached (
+        (struct hash_table *) &set->table, &var_key);
+    }
+# ifdef KMK
+  RESOLVE_ALIAS_VARIABLE(v);
+# endif
+  return v;
 #endif /* CONFIG_WITH_STRCACHE2 */
 }
@@ -1196,5 +1347,14 @@
 	  {
 	    /* GKM FIXME: delete in from_set->table */
-	    free (from_var->value);
+#ifdef KMK
+            if (from_var->aliased)
+              fatal(NULL, ("Attempting to delete aliased variable '%s'"), from_var->name);
+            if (from_var->alias)
+              fatal(NULL, ("Attempting to delete variable aliased '%s'"), from_var->name);
+#endif
+#ifdef CONFIG_WITH_RDONLY_VARIABLE_VALUE
+            if (!from_var->rdonly_val)
+#endif
+              free (from_var->value);
 	    free (from_var);
 	  }
@@ -1891,4 +2051,7 @@
   unsigned int new_value_len = value_len + (v->value_length != 0 ? 1 + v->value_length : 0);
   int done_1st_prepend_copy = 0;
+#ifdef KMK
+  assert (!v->alias);
+#endif
 
   /* Drop empty strings. Use $(NO_SUCH_VARIABLE) if a space is wanted. */
@@ -2607,4 +2770,8 @@
   const char *prefix = arg;
   const char *origin;
+#ifdef KMK
+  const struct variable *alias = v;
+  RESOLVE_ALIAS_VARIABLE(v);
+#endif
 
   switch (v->origin)
@@ -2644,7 +2811,18 @@
   if (v->private_var)
     fputs (" private", stdout);
+#ifndef KMK
   if (v->fileinfo.filenm)
     printf (_(" (from `%s', line %lu)"),
             v->fileinfo.filenm, v->fileinfo.lineno);
+#else  /* KMK */
+  if (alias->fileinfo.filenm)
+    printf (_(" (from '%s', line %lu)"),
+            alias->fileinfo.filenm, alias->fileinfo.lineno);
+  if (alias->aliased)
+    fputs (" aliased", stdout);
+  if (alias->alias)
+    printf (_(", alias for '%s'"), v->name);
+#endif /* KMK */
+
 #ifdef CONFIG_WITH_MAKE_STATS
   if (v->changes != 0)
@@ -2669,10 +2847,18 @@
   /* Is this a `define'?  */
   if (v->recursive && strchr (v->value, '\n') != 0)
+#ifndef KMK /** @todo language feature for aliases */
     printf ("define %s\n%s\nendef\n", v->name, v->value);
+#else
+    printf ("define %s\n%s\nendef\n", alias->name, v->value);
+#endif
   else
     {
       char *p;
 
+#ifndef KMK /** @todo language feature for aliases */
       printf ("%s %s= ", v->name, v->recursive ? v->append ? "+" : "" : ":");
+#else
+      printf ("%s %s= ", alias->name, v->recursive ? v->append ? "+" : "" : ":");
+#endif
 
       /* Check if the value is just whitespace.  */
Index: trunk/src/kmk/variable.h
===================================================================
--- trunk/src/kmk/variable.h	(revision 2717)
+++ trunk/src/kmk/variable.h	(revision 2718)
@@ -89,4 +89,8 @@
 #ifdef CONFIG_WITH_RDONLY_VARIABLE_VALUE
     unsigned int rdonly_val:1;  /* VALUE is read only (strcache/const). */
+#endif
+#ifdef KMK
+    unsigned int alias:1;       /* Nonzero if alias. VALUE points to the real variable. */
+    unsigned int aliased:1;     /* Nonzero if aliased. Cannot be undefined. */
 #endif
     enum variable_flavor
@@ -450,4 +454,11 @@
           undefine_variable_in_set((n),(l),(o),NULL)
 
+#ifdef KMK
+struct variable *
+define_variable_alias_in_set (const char *name, unsigned int length,
+                              struct variable *target, enum variable_origin origin,
+                              struct variable_set *set, const struct floc *flocp);
+#endif
+
 /* Warn that NAME is an undefined variable.  */
 
