summaryrefslogtreecommitdiffstats
path: root/src/kmk/kbuild-object.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kmk/kbuild-object.c')
-rw-r--r--src/kmk/kbuild-object.c1409
1 files changed, 1409 insertions, 0 deletions
diff --git a/src/kmk/kbuild-object.c b/src/kmk/kbuild-object.c
new file mode 100644
index 0000000..e295636
--- /dev/null
+++ b/src/kmk/kbuild-object.c
@@ -0,0 +1,1409 @@
+/* $Id: kbuild-object.c 3141 2018-03-14 21:58:32Z bird $ */
+/** @file
+ * kBuild objects.
+ */
+
+/*
+ * Copyright (c) 2011-2014 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
+ *
+ * This file is part of kBuild.
+ *
+ * kBuild is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * kBuild 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kBuild. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/* No GNU coding style here! */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include "makeint.h"
+#include "filedef.h"
+#include "variable.h"
+#include "dep.h"
+#include "debug.h"
+#include "kbuild.h"
+
+#include <assert.h>
+#include <stdarg.h>
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+#define WORD_IS(a_pszWord, a_cchWord, a_szWord2) \
+ ( (a_cchWord) == sizeof(a_szWord2) - 1 && memcmp((a_pszWord), a_szWord2, sizeof(a_szWord2) - 1) == 0)
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/** kBuild object type. */
+enum kBuildType
+{
+ kBuildType_Invalid,
+ kBuildType_Target,
+ kBuildType_Template,
+ kBuildType_Tool,
+ kBuildType_Sdk,
+ kBuildType_Unit
+};
+
+enum kBuildSeverity
+{
+ kBuildSeverity_Warning,
+ kBuildSeverity_Error,
+ kBuildSeverity_Fatal
+};
+
+
+/**
+ * kBuild object data.
+ */
+struct kbuild_object
+{
+ /** The object type. */
+ enum kBuildType enmType;
+ /** Object name length. */
+ size_t cchName;
+ /** The bare name of the define. */
+ char *pszName;
+ /** The file location where this define was declared. */
+ floc FileLoc;
+
+ /** Pointer to the next element in the global list. */
+ struct kbuild_object *pGlobalNext;
+
+ /** The variable set associated with this define. */
+ struct variable_set_list *pVariables;
+
+ /** The parent name, NULL if none. */
+ char *pszParent;
+ /** The length of the parent name. */
+ size_t cchParent;
+ /** Pointer to the parent. Resolved lazily, so it can be NULL even if we
+ * have a parent. */
+ struct kbuild_object *pParent;
+
+ /** The template, NULL if none. Only applicable to targets. Only covers the
+ * primary template, not target or type specific templates.
+ * @todo not sure if this is really necessary. */
+ char const *pszTemplate;
+
+ /** The variable prefix. */
+ char *pszVarPrefix;
+ /** The length of the variable prefix. */
+ size_t cchVarPrefix;
+};
+
+
+/**
+ * The data we stack during eval.
+ */
+struct kbuild_eval_data
+{
+ /** Pointer to the element below us on the stack. */
+ struct kbuild_eval_data *pStackDown;
+ /** Pointer to the object. */
+ struct kbuild_object *pObj;
+ /** The saved current variable set, for restoring in kBuild-endef. */
+ struct variable_set_list *pVariablesSaved;
+};
+
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** Linked list (LIFO) of kBuild defines.
+ * @todo use a hash! */
+static struct kbuild_object *g_pHeadKbObjs = NULL;
+/** Stack of kBuild evalutation contexts.
+ * This is for dealing with potential recursive kBuild object definition,
+ * generally believed to only happen via $(eval ) or include similar. */
+struct kbuild_eval_data *g_pTopKbEvalData = NULL;
+
+/** Cached variable name '_TEMPLATE'. */
+static const char *g_pszVarNmTemplate = NULL;
+
+/** Zero if compatibility mode is disabled, non-zero if enabled.
+ * If explicitily enabled, the value will be greater than 1. */
+int g_fKbObjCompMode = 1;
+
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+static struct kbuild_object *
+resolve_kbuild_object_parent(struct kbuild_object *pObj, int fQuiet);
+static struct kbuild_object *
+get_kbuild_object_parent(struct kbuild_object *pObj, enum kBuildSeverity enmSeverity);
+
+static struct kbuild_object *
+parse_kbuild_object_variable_accessor(const char *pchExpr, size_t cchExpr,
+ enum kBuildSeverity enmSeverity, const floc *pFileLoc,
+ const char **ppchVarNm, size_t *pcchVarNm, enum kBuildType *penmType);
+
+
+/**
+ * Initializes the kBuild object stuff.
+ *
+ * Requires the variable_cache to be initialized.
+ */
+void init_kbuild_object(void)
+{
+ g_pszVarNmTemplate = strcache2_add(&variable_strcache, STRING_SIZE_TUPLE("_TEMPLATE"));
+}
+
+
+/**
+ * Reports a problem with dynamic severity level.
+ *
+ * @param enmSeverity The severity level.
+ * @param pFileLoc The file location.
+ * @param pszFormat The format string.
+ * @param ... Arguments for the format string.
+ */
+static void kbuild_report_problem(enum kBuildSeverity enmSeverity, const floc *pFileLoc,
+ const char *pszFormat, ...)
+{
+ char szBuf[8192];
+ va_list va;
+
+ va_start(va, pszFormat);
+#ifdef _MSC_VER
+ _vsnprintf(szBuf, sizeof(szBuf), pszFormat, va);
+#else
+ vsnprintf(szBuf, sizeof(szBuf), pszFormat, va);
+#endif
+ va_end(va);
+
+ switch (enmSeverity)
+ {
+ case kBuildSeverity_Warning:
+ OS(message, 0, "%s", szBuf);
+ break;
+ case kBuildSeverity_Error:
+ OS(error, pFileLoc, "%s", szBuf);
+ break;
+ default:
+ case kBuildSeverity_Fatal:
+ OS(fatal, pFileLoc, "%s", szBuf);
+ break;
+ }
+}
+
+
+static const char *
+eval_kbuild_type_to_string(enum kBuildType enmType)
+{
+ switch (enmType)
+ {
+ case kBuildType_Target: return "target";
+ case kBuildType_Template: return "template";
+ case kBuildType_Tool: return "tool";
+ case kBuildType_Sdk: return "sdk";
+ case kBuildType_Unit: return "unit";
+ default:
+ case kBuildType_Invalid: return "invalid";
+ }
+}
+
+/**
+ * Gets the length of the string representation of the given type.
+ *
+ * @returns The string length.
+ * @param enmType The kBuild object type in question.
+ */
+static unsigned
+eval_kbuild_type_to_string_length(enum kBuildType enmType)
+{
+ switch (enmType)
+ {
+ case kBuildType_Target: return sizeof("target") - 1;
+ case kBuildType_Template: return sizeof("template") - 1;
+ case kBuildType_Tool: return sizeof("tool") - 1;
+ case kBuildType_Sdk: return sizeof("sdk") - 1;
+ case kBuildType_Unit: return sizeof("unit") - 1;
+ default:
+ case kBuildType_Invalid: return sizeof("invalid") - 1;
+ }
+}
+
+/**
+ * Converts a string into an kBuild object type.
+ *
+ * @returns The type on success, kBuildType_Invalid on failure.
+ * @param pchWord The pchWord. Not necessarily zero terminated.
+ * @param cchWord The length of the word.
+ */
+static enum kBuildType
+eval_kbuild_type_from_string(const char *pchWord, size_t cchWord)
+{
+ if (cchWord >= 3)
+ {
+ if (*pchWord == 't')
+ {
+ if (WORD_IS(pchWord, cchWord, "target"))
+ return kBuildType_Target;
+ if (WORD_IS(pchWord, cchWord, "template"))
+ return kBuildType_Template;
+ if (WORD_IS(pchWord, cchWord, "tool"))
+ return kBuildType_Tool;
+ }
+ else
+ {
+ if (WORD_IS(pchWord, cchWord, "sdk"))
+ return kBuildType_Sdk;
+ if (WORD_IS(pchWord, cchWord, "unit"))
+ return kBuildType_Unit;
+ }
+ }
+
+ return kBuildType_Invalid;
+}
+
+
+
+#if 0 /* unused */
+/**
+ * Helper function for caching variable name strings.
+ *
+ * @returns The string cache variable name.
+ * @param pszName The variable name.
+ * @param ppszCache Cache variable, static or global. Initialize to
+ * NULL.
+ */
+static const char *
+kbuild_variable_name(const char *pszName, const char **ppszCache)
+{
+ const char *pszRet = *ppszCache;
+ if (!pszRet)
+ *ppszCache = pszRet = strcache2_add(&variable_strcache, pszName, strlen(pszName));
+ return pszRet;
+}
+#endif
+
+static struct kbuild_object *
+lookup_kbuild_object(enum kBuildType enmType, const char *pchName, size_t cchName)
+{
+ /* Linear lookup for now. */
+ struct kbuild_object *pCur = g_pHeadKbObjs;
+ while (pCur)
+ {
+ if ( pCur->enmType == enmType
+ && pCur->cchName == cchName
+ && !memcmp(pCur->pszName, pchName, cchName))
+ return pCur;
+ pCur = pCur->pGlobalNext;
+ }
+ return NULL;
+}
+
+
+/** @name Defining and modifying variables
+ * @{
+ */
+
+/**
+ * Checks if the variable name is valid.
+ *
+ * @returns 1 if valid, 0 if not.
+ * @param pchName The variable name.
+ * @param cchName The length of the variable name.
+ */
+static int
+is_valid_kbuild_object_variable_name(const char *pchName, size_t cchName)
+{
+ if (cchName > 0)
+ {
+ if (!memchr(pchName, '[', cchName))
+ {
+ /** @todo more? */
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static const char *
+kbuild_replace_special_accessors(const char *pchValue, size_t *pcchValue, int *pfDuplicateValue,
+ const floc *pFileLoc)
+{
+ size_t cchValue = *pcchValue;
+ size_t cbAllocated = *pfDuplicateValue ? 0 : cchValue + 1;
+
+ /*
+ * Loop thru each potential special accessor occurance in the string.
+ *
+ * Unfortunately, we don't have a strnstr function in the C library, so
+ * we'll using memchr and doing a few more rounds in this loop.
+ */
+ size_t cchLeft = cchValue;
+ char *pchLeft = (char *)pchValue;
+ for (;;)
+ {
+ int fSuper;
+ char *pch = (char *)memchr(pchLeft, '$', cchLeft);
+ if (!pch)
+ break;
+
+ pch++;
+ cchLeft -= pch - pchLeft;
+ pchLeft = pch;
+
+ /* [@self] is the shorter, quit if there isn't enough room for even it. */
+ if (cchLeft < sizeof("([@self]") - 1)
+ break;
+
+ /* We don't care how many dollars there are in front of a special accessor. */
+ if (*pchLeft == '$')
+ {
+ do
+ {
+ cchLeft--;
+ pchLeft++;
+ } while (cchLeft >= sizeof("([@self]") - 1 && *pchLeft == '$');
+ if (cchLeft < sizeof("([@self]") - 1)
+ break;
+ }
+
+ /* Is it a special accessor? */
+ if ( pchLeft[2] != '@'
+ || pchLeft[1] != '['
+ || pchLeft[0] != '(')
+ continue;
+ pchLeft += 2;
+ cchLeft -= 2;
+ if (!memcmp(pchLeft, STRING_SIZE_TUPLE("@self]")))
+ fSuper = 0;
+ else if ( cchLeft >= sizeof("@super]")
+ && !memcmp(pchLeft, STRING_SIZE_TUPLE("@super]")))
+ fSuper = 1;
+ else
+ continue;
+
+ /*
+ * We've got something to replace. First figure what with and then
+ * resize the value buffer.
+ */
+ if (g_pTopKbEvalData)
+ {
+ struct kbuild_object *pObj = g_pTopKbEvalData->pObj;
+ size_t const cchSpecial = fSuper ? sizeof("@super") - 1 : sizeof("@self") - 1;
+ size_t cchName;
+ size_t cchType;
+ long cchDelta;
+ const char *pszName;
+
+ if (fSuper)
+ {
+ pObj = get_kbuild_object_parent(pObj, kBuildSeverity_Error);
+ if (!pObj)
+ continue;
+ }
+ pszName = pObj->pszName;
+ cchName = pObj->cchName;
+ cchType = eval_kbuild_type_to_string_length(pObj->enmType);
+ cchDelta = cchType + 1 + cchName - cchSpecial;
+
+ if (cchValue + cchDelta >= cbAllocated)
+ {
+ size_t offLeft = pchLeft - pchValue;
+ char *pszNewValue;
+
+ cbAllocated = cchValue + cchDelta + 1;
+ if (cchValue < 1024)
+ cbAllocated = (cbAllocated + 31) & ~(size_t)31;
+ else
+ cbAllocated = (cbAllocated + 255) & ~(size_t)255;
+ pszNewValue = (char *)xmalloc(cbAllocated);
+
+ memcpy(pszNewValue, pchValue, offLeft);
+ memcpy(pszNewValue + offLeft + cchSpecial + cchDelta,
+ pchLeft + cchSpecial,
+ cchLeft - cchSpecial + 1);
+
+ if (*pfDuplicateValue == 0)
+ free((char *)pchValue);
+ else
+ *pfDuplicateValue = 0;
+
+ pchValue = pszNewValue;
+ pchLeft = pszNewValue + offLeft;
+ }
+ else
+ {
+ assert(*pfDuplicateValue == 0);
+ memmove(pchLeft + cchSpecial + cchDelta,
+ pchLeft + cchSpecial,
+ cchLeft - cchSpecial + 1);
+ }
+
+ cchLeft += cchDelta;
+ cchValue += cchDelta;
+ *pcchValue = cchValue;
+
+ memcpy(pchLeft, eval_kbuild_type_to_string(pObj->enmType), cchType);
+ pchLeft += cchType;
+ *pchLeft++ = '@';
+ memcpy(pchLeft, pszName, cchName);
+ pchLeft += cchName;
+ cchLeft -= cchType + 1 + cchName;
+ }
+ else
+ error(pFileLoc, 20, _("The '$([%.*s...' accessor can only be used in the context of a kBuild object"),
+ (int)MIN(cchLeft, 20), pchLeft);
+ }
+
+ return pchValue;
+}
+
+static struct variable *
+define_kbuild_object_variable_cached(struct kbuild_object *pObj, const char *pszName,
+ const char *pchValue, size_t cchValue,
+ int fDuplicateValue, enum variable_origin enmOrigin,
+ int fRecursive, int fNoSpecialAccessors, const floc *pFileLoc)
+{
+ struct variable *pVar;
+ size_t cchName = strcache2_get_len(&variable_strcache, pszName);
+
+ if (fRecursive && !fNoSpecialAccessors)
+ pchValue = kbuild_replace_special_accessors(pchValue, &cchValue, &fDuplicateValue, pFileLoc);
+
+ pVar = define_variable_in_set(pszName, cchName,
+ pchValue, cchValue, fDuplicateValue,
+ enmOrigin, fRecursive,
+ pObj->pVariables->set,
+ pFileLoc);
+
+ /* Single underscore prefixed variables gets a global alias. */
+ if ( pszName[0] == '_'
+ && pszName[1] != '_'
+ && g_fKbObjCompMode)
+ {
+ 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';
+
+ pAlias = define_variable_alias_in_set(pszPrefixed, cchPrefixed, pVar, enmOrigin,
+ &global_variable_set, pFileLoc);
+ if (!pAlias->alias)
+ OS(error, pFileLoc, _("Error defining alias '%s'"), pszPrefixed);
+ }
+
+ return pVar;
+}
+
+#if 0
+struct variable *
+define_kbuild_object_variable(struct kbuild_object *pObj, const char *pchName, size_t cchName,
+ const char *pchValue, size_t cchValue,
+ int fDuplicateValue, enum variable_origin enmOrigin,
+ int fRecursive, const floc *pFileLoc)
+{
+ return define_kbuild_object_variable_cached(pObj, strcache2_add(&variable_strcache, pchName, cchName),
+ pchValue, cchValue,
+ fDuplicateValue, enmOrigin,
+ fRecursive, pFileLoc);
+}
+#endif
+
+/**
+ * Try define a kBuild object variable via a possible accessor
+ * ([type@object]var).
+ *
+ * @returns Pointer to the defined variable on success.
+ * @retval VAR_NOT_KBUILD_ACCESSOR if it isn't an accessor.
+ *
+ * @param pchName The variable name, not cached.
+ * @param cchName The variable name length. This will not be ~0U.
+ * @param pszValue The variable value. If @a fDuplicateValue is clear,
+ * this should be assigned as the actual variable
+ * value, otherwise it will be duplicated. In the
+ * latter case it might not be properly null
+ * terminated.
+ * @param cchValue The value length.
+ * @param fDuplicateValue Whether @a pszValue need to be duplicated on the
+ * heap or is already there.
+ * @param enmOrigin The variable origin.
+ * @param fRecursive Whether it's a recursive variable.
+ * @param pFileLoc The location of the variable definition.
+ */
+struct variable *
+try_define_kbuild_object_variable_via_accessor(const char *pchName, size_t cchName,
+ const char *pszValue, size_t cchValue, int fDuplicateValue,
+ enum variable_origin enmOrigin, int fRecursive,
+ floc const *pFileLoc)
+{
+ struct kbuild_object *pObj;
+ const char *pchVarNm;
+ size_t cchVarNm;
+
+ pObj = parse_kbuild_object_variable_accessor(pchName, cchName, kBuildSeverity_Fatal, pFileLoc,
+ &pchVarNm, &cchVarNm, NULL);
+ if (pObj != KOBJ_NOT_KBUILD_ACCESSOR)
+ {
+ assert(pObj != NULL);
+ if (!is_valid_kbuild_object_variable_name(pchVarNm, cchVarNm))
+ fatal(pFileLoc, cchVarNm + cchName, _("Invalid kBuild object variable name: '%.*s' ('%.*s')"),
+ (int)cchVarNm, pchVarNm, (int)cchName, pchName);
+ return define_kbuild_object_variable_cached(pObj, strcache2_add(&variable_strcache, pchVarNm, cchVarNm),
+ pszValue, cchValue, fDuplicateValue, enmOrigin, fRecursive,
+ 0 /*fNoSpecialAccessors*/, pFileLoc);
+ }
+
+ return VAR_NOT_KBUILD_ACCESSOR;
+}
+
+/**
+ * Define a kBuild object variable in the topmost kBuild object.
+ *
+ * This won't be an variable accessor.
+ *
+ * @returns Pointer to the defined variable on success.
+ *
+ * @param pchName The variable name, not cached.
+ * @param cchName The variable name length. This will not be ~0U.
+ * @param pszValue The variable value. If @a fDuplicateValue is clear,
+ * this should be assigned as the actual variable
+ * value, otherwise it will be duplicated. In the
+ * latter case it might not be properly null
+ * terminated.
+ * @param cchValue The value length.
+ * @param fDuplicateValue Whether @a pszValue need to be duplicated on the
+ * heap or is already there.
+ * @param enmOrigin The variable origin.
+ * @param fRecursive Whether it's a recursive variable.
+ * @param pFileLoc The location of the variable definition.
+ */
+struct variable *
+define_kbuild_object_variable_in_top_obj(const char *pchName, size_t cchName,
+ const char *pszValue, size_t cchValue, int fDuplicateValue,
+ enum variable_origin enmOrigin, int fRecursive,
+ floc const *pFileLoc)
+{
+ assert(g_pTopKbEvalData != NULL);
+
+ if (!is_valid_kbuild_object_variable_name(pchName, cchName))
+ fatal(pFileLoc, cchName, _("Invalid kBuild object variable name: '%.*s'"), (int)cchName, pchName);
+
+ return define_kbuild_object_variable_cached(g_pTopKbEvalData->pObj, strcache2_add(&variable_strcache, pchName, cchName),
+ pszValue, cchValue, fDuplicateValue, enmOrigin, fRecursive,
+ 0 /*fNoSpecialAccessors*/, pFileLoc);
+}
+
+/**
+ * Implements appending and prepending to a kBuild object variable.
+ *
+ * The variable is either accessed thru an accessor or by the topmost kBuild
+ * object.
+ *
+ * @returns Pointer to the defined variable on success.
+ *
+ * @param pchName The variable name, not cached.
+ * @param cchName The variable name length. This will not be ~0U.
+ * @param pszValue The variable value. Must be duplicated.
+ * @param cchValue The value length.
+ * @param fSimpleValue Whether we've already figured that it's a simple
+ * value. This is for optimizing appending/prepending
+ * to an existing simple value variable.
+ * @param enmOrigin The variable origin.
+ * @param fAppend Append if set, prepend if clear.
+ * @param pFileLoc The location of the variable definition.
+ */
+struct variable *
+kbuild_object_variable_pre_append(const char *pchName, size_t cchName,
+ const char *pchValue, size_t cchValue, int fSimpleValue,
+ enum variable_origin enmOrigin, int fAppend,
+ const floc *pFileLoc)
+{
+ struct kbuild_object *pObj;
+ struct variable VarKey;
+
+ /*
+ * Resolve the relevant kBuild object first.
+ */
+ if (cchName > 3 && pchName[0] == '[')
+ {
+ const char *pchVarNm;
+ size_t cchVarNm;
+ pObj = parse_kbuild_object_variable_accessor(pchName, cchName, kBuildSeverity_Fatal, pFileLoc,
+ &pchVarNm, &cchVarNm, NULL);
+ if (pObj != KOBJ_NOT_KBUILD_ACCESSOR)
+ {
+ pchName = pchVarNm;
+ cchName = cchVarNm;
+ }
+ else
+ pObj = g_pTopKbEvalData->pObj;
+ }
+ else
+ pObj = g_pTopKbEvalData->pObj;
+
+ /*
+ * Make sure the variable name is valid. Raise fatal error if not.
+ */
+ if (!is_valid_kbuild_object_variable_name(pchName, cchName))
+ fatal(pFileLoc, cchName, _("Invalid kBuild object variable name: '%.*s'"), (int)cchName, pchName);
+
+ /*
+ * Get the cached name and look it up in the object's variables.
+ */
+ VarKey.name = strcache2_lookup(&variable_strcache, pchName, cchName);
+ if (VarKey.name)
+ {
+ struct variable *pVar;
+
+ VarKey.length = cchName;
+ pVar = (struct variable *)hash_find_item_strcached(&pObj->pVariables->set->table, &VarKey);
+ if (pVar)
+ {
+ /* Append/prepend to existing variable. */
+ int fDuplicateValue = 1;
+ if (pVar->recursive && !fSimpleValue)
+ pchValue = kbuild_replace_special_accessors(pchValue, &cchValue, &fDuplicateValue, pFileLoc);
+
+ pVar = do_variable_definition_append(pFileLoc, pVar, pchValue, cchValue, fSimpleValue, enmOrigin, fAppend);
+
+ if (fDuplicateValue == 0)
+ free((char *)pchValue);
+ return pVar;
+ }
+
+ /*
+ * Not found. Check ancestors if the 'override' directive isn't applied.
+ */
+ if (pObj->pszParent && enmOrigin != o_override)
+ {
+ struct kbuild_object *pParent = pObj;
+ for (;;)
+ {
+ pParent = resolve_kbuild_object_parent(pParent, 0 /*fQuiet*/);
+ if (!pParent)
+ break;
+
+ pVar = (struct variable *)hash_find_item_strcached(&pParent->pVariables->set->table, &VarKey);
+ if (pVar)
+ {
+ if (pVar->value_length != ~0U)
+ assert(pVar->value_length == strlen(pVar->value));
+ else
+ pVar->value_length = strlen(pVar->value);
+
+ /*
+ * Combine the two values and define the variable in the
+ * specified child object. We must disregard 'origin' a
+ * little here, so we must do the gritty stuff our selves.
+ */
+ if ( pVar->recursive
+ || fSimpleValue
+ || !cchValue
+ || memchr(pchValue, '$', cchValue) == NULL )
+ {
+ int fDuplicateValue = 1;
+ size_t cchNewValue;
+ char *pszNewValue;
+ char *pszTmp;
+
+ /* Just join up the two values. */
+ if (pVar->recursive && !fSimpleValue)
+ pchValue = kbuild_replace_special_accessors(pchValue, &cchValue, &fDuplicateValue, pFileLoc);
+ if (pVar->value_length == 0)
+ {
+ cchNewValue = cchValue;
+ pszNewValue = xstrndup(pchValue, cchValue);
+ }
+ else if (!cchValue)
+ {
+ cchNewValue = pVar->value_length;
+ pszNewValue = xmalloc(cchNewValue + 1);
+ memcpy(pszNewValue, pVar->value, cchNewValue + 1);
+ }
+ else
+ {
+ cchNewValue = pVar->value_length + 1 + cchValue;
+ pszNewValue = xmalloc(cchNewValue + 1);
+ if (fAppend)
+ {
+ memcpy(pszNewValue, pVar->value, pVar->value_length);
+ pszTmp = pszNewValue + pVar->value_length;
+ *pszTmp++ = ' ';
+ memcpy(pszTmp, pchValue, cchValue);
+ pszTmp[cchValue] = '\0';
+ }
+ else
+ {
+ memcpy(pszNewValue, pchValue, cchValue);
+ pszTmp = pszNewValue + cchValue;
+ *pszTmp++ = ' ';
+ memcpy(pszNewValue, pVar->value, pVar->value_length);
+ pszTmp[pVar->value_length] = '\0';
+ }
+ }
+
+ /* Define the new variable in the child. */
+ pVar = define_kbuild_object_variable_cached(pObj, VarKey.name,
+ pszNewValue, cchNewValue, 0 /*fDuplicateValue*/,
+ enmOrigin, pVar->recursive, 1 /*fNoSpecialAccessors*/,
+ pFileLoc);
+ if (fDuplicateValue == 0)
+ free((char *)pchValue);
+ }
+ else
+ {
+ /* Lazy bird: Copy the variable from the ancestor and
+ then do a normal append/prepend on it. */
+ pVar = define_kbuild_object_variable_cached(pObj, VarKey.name,
+ pVar->value, pVar->value_length, 1 /*fDuplicateValue*/,
+ enmOrigin, pVar->recursive, 1 /*fNoSpecialAccessors*/,
+ pFileLoc);
+ append_expanded_string_to_variable(pVar, pchValue, cchValue, fAppend);
+ }
+ return pVar;
+ }
+ }
+ }
+ }
+ else
+ VarKey.name = strcache2_add(&variable_strcache, pchName, cchName);
+
+ /* Variable not found. */
+ return define_kbuild_object_variable_cached(pObj, VarKey.name,
+ pchValue, cchValue, 1 /*fDuplicateValue*/, enmOrigin,
+ 1 /*fRecursive */, 0 /*fNoSpecialAccessors*/, pFileLoc);
+}
+
+/** @} */
+
+
+static char *
+allocate_expanded_next_token(const char **ppszCursor, const char *pszEos, size_t *pcchToken, int fStrip)
+{
+ unsigned int cchToken;
+ char *pszToken = find_next_token_eos(ppszCursor, pszEos, &cchToken);
+ if (pszToken)
+ {
+ pszToken = allocated_variable_expand_2(pszToken, cchToken, &cchToken);
+ if (pszToken)
+ {
+ if (fStrip)
+ {
+ unsigned int off = 0;
+ while (MY_IS_BLANK(pszToken[off]))
+ off++;
+ if (off)
+ {
+ cchToken -= off;
+ memmove(pszToken, &pszToken[off], cchToken + 1);
+ }
+
+ while (cchToken > 0 && MY_IS_BLANK(pszToken[cchToken - 1]))
+ pszToken[--cchToken] = '\0';
+ }
+
+ assert(cchToken == strlen(pszToken));
+ if (pcchToken)
+ *pcchToken = cchToken;
+ return pszToken;
+ }
+ }
+
+ if (pcchToken)
+ *pcchToken = 0;
+ return NULL;
+}
+
+static struct kbuild_object *
+resolve_kbuild_object_parent(struct kbuild_object *pObj, int fQuiet)
+{
+ if ( !pObj->pParent
+ && pObj->pszParent)
+ {
+ struct kbuild_object *pCur = g_pHeadKbObjs;
+ while (pCur)
+ {
+ if ( pCur->enmType == pObj->enmType
+ && !strcmp(pCur->pszName, pObj->pszParent))
+ {
+ if ( pCur->pszParent
+ && ( pCur->pParent == pObj
+ || !strcmp(pCur->pszParent, pObj->pszName)) )
+ OSS(fatal, &pObj->FileLoc, _("'%s' and '%s' are both trying to be each other children..."),
+ pObj->pszName, pCur->pszName);
+
+ pObj->pParent = pCur;
+ pObj->pVariables->next = pObj->pVariables;
+ return pCur;
+ }
+
+ pCur = pCur->pGlobalNext;
+ }
+
+ /* Not found. */
+ if (!fQuiet)
+ OSS(error, &pObj->FileLoc, _("Could not locate parent '%s' of '%s'"), pObj->pszParent, pObj->pszName);
+ }
+ return pObj->pParent;
+}
+
+/**
+ * Get the parent of the given object, it is expected to have one.
+ *
+ * @returns Pointer to the parent. NULL if we survive failure.
+ * @param pObj The kBuild object.
+ * @param enmSeverity The severity of a missing parent.
+ */
+static struct kbuild_object *
+get_kbuild_object_parent(struct kbuild_object *pObj, enum kBuildSeverity enmSeverity)
+{
+ struct kbuild_object *pParent = pObj->pParent;
+ if (pParent)
+ return pParent;
+
+ pParent = resolve_kbuild_object_parent(pObj, 1 /*fQuiet - complain below */);
+ if (pParent)
+ return pParent;
+
+ if (pObj->pszParent)
+ kbuild_report_problem(enmSeverity, &pObj->FileLoc,
+ _("Could not local parent '%s' for kBuild object '%s'"),
+ pObj->pszParent, pObj->pszName);
+ else
+ kbuild_report_problem(enmSeverity, &pObj->FileLoc,
+ _("kBuild object '%s' has no parent ([@super])"),
+ pObj->pszName);
+ return NULL;
+}
+
+static int
+eval_kbuild_define_xxxx(struct kbuild_eval_data **ppData, const floc *pFileLoc,
+ const char *pszLine, const char *pszEos, int fIgnoring, enum kBuildType enmType)
+{
+ unsigned int cch;
+ char ch;
+ char *psz;
+ const char *pszPrefix;
+ struct kbuild_object *pObj;
+ struct kbuild_eval_data *pData;
+
+ if (fIgnoring)
+ return 0;
+
+ /*
+ * Create a new kBuild object.
+ */
+ pObj = xmalloc(sizeof(*pObj));
+ pObj->enmType = enmType;
+ pObj->pszName = NULL;
+ pObj->cchName = 0;
+ pObj->FileLoc = *pFileLoc;
+
+ pObj->pGlobalNext = g_pHeadKbObjs;
+ g_pHeadKbObjs = pObj;
+
+ pObj->pVariables = create_new_variable_set();
+
+ pObj->pszParent = NULL;
+ pObj->cchParent = 0;
+ pObj->pParent = NULL;
+
+ pObj->pszTemplate = NULL;
+
+ pObj->pszVarPrefix = NULL;
+ pObj->cchVarPrefix = 0;
+
+ /*
+ * The first word is the name.
+ */
+ pObj->pszName = allocate_expanded_next_token(&pszLine, pszEos, &pObj->cchName, 1 /*strip*/);
+ if (!pObj->pszName || !*pObj->pszName)
+ O(fatal, pFileLoc, _("The kBuild define requires a name"));
+
+ psz = pObj->pszName;
+ while ((ch = *psz++) != '\0')
+ if (!isgraph(ch))
+ {
+ OSS(error, pFileLoc, _("The 'kBuild-define-%s' name '%s' contains one or more invalid characters"),
+ eval_kbuild_type_to_string(enmType), pObj->pszName);
+ break;
+ }
+
+ /*
+ * Calc the variable prefix.
+ */
+ switch (enmType)
+ {
+ case kBuildType_Target: pszPrefix = ""; break;
+ case kBuildType_Template: pszPrefix = "TEMPLATE_"; break;
+ case kBuildType_Tool: pszPrefix = "TOOL_"; break;
+ case kBuildType_Sdk: pszPrefix = "SDK_"; break;
+ case kBuildType_Unit: pszPrefix = "UNIT_"; break;
+ default:
+ ON(fatal, pFileLoc, _("enmType=%d"), enmType);
+ return -1;
+ }
+ cch = strlen(pszPrefix);
+ pObj->cchVarPrefix = cch + pObj->cchName;
+ pObj->pszVarPrefix = xmalloc(pObj->cchVarPrefix + 1);
+ memcpy(pObj->pszVarPrefix, pszPrefix, cch);
+ memcpy(&pObj->pszVarPrefix[cch], pObj->pszName, pObj->cchName);
+
+ /*
+ * Parse subsequent words.
+ */
+ psz = find_next_token_eos(&pszLine, pszEos, &cch);
+ while (psz)
+ {
+ if (WORD_IS(psz, cch, "extending"))
+ {
+ /* Inheritance directive. */
+ if (pObj->pszParent != NULL)
+ O(fatal, pFileLoc, _("'extending' can only occure once"));
+ pObj->pszParent = allocate_expanded_next_token(&pszLine, pszEos, &pObj->cchParent, 1 /*strip*/);
+ if (!pObj->pszParent || !*pObj->pszParent)
+ O(fatal, pFileLoc, _("'extending' requires a parent name"));
+ }
+ else if (WORD_IS(psz, cch, "using"))
+ {
+ char *pszTemplate;
+ size_t cchTemplate;
+
+ /* Template directive. */
+ if (enmType != kBuildType_Target)
+ O(fatal, pFileLoc, _("'using <template>' can only be used with 'kBuild-define-target'"));
+ if (pObj->pszTemplate != NULL)
+ O(fatal, pFileLoc, _("'using' can only occure once"));
+
+ pszTemplate = allocate_expanded_next_token(&pszLine, pszEos, &cchTemplate, 1 /*fStrip*/);
+ if (!pszTemplate || !*pszTemplate)
+ O(fatal, pFileLoc, _("'using' requires a template name"));
+
+ define_kbuild_object_variable_cached(pObj, g_pszVarNmTemplate, pszTemplate, cchTemplate,
+ 0 /*fDuplicateValue*/, o_default, 0 /*fRecursive*/,
+ 1 /*fNoSpecialAccessors*/, pFileLoc);
+
+ }
+ else
+ fatal(pFileLoc, cch, _("Don't know what '%.*s' means"), (int)cch, psz);
+
+ /* next token */
+ psz = find_next_token_eos(&pszLine, pszEos, &cch);
+ }
+
+ /*
+ * Try resolve the parent.
+ */
+ resolve_kbuild_object_parent(pObj, 1 /*fQuiet*/);
+
+ /*
+ * Create an eval stack entry and change the current variable set.
+ */
+ pData = xmalloc(sizeof(*pData));
+ pData->pObj = pObj;
+ pData->pVariablesSaved = current_variable_set_list;
+ current_variable_set_list = pObj->pVariables;
+
+ pData->pStackDown = *ppData;
+ *ppData = pData;
+ g_pTopKbEvalData = pData;
+
+ return 0;
+}
+
+static int
+eval_kbuild_endef_xxxx(struct kbuild_eval_data **ppData, const floc *pFileLoc,
+ const char *pszLine, const char *pszEos, int fIgnoring, enum kBuildType enmType)
+{
+ struct kbuild_eval_data *pData;
+ struct kbuild_object *pObj;
+ size_t cchName;
+ char *pszName;
+
+ if (fIgnoring)
+ return 0;
+
+ /*
+ * Is there something to pop?
+ */
+ pData = *ppData;
+ if (!pData)
+ {
+ OSS(error, pFileLoc, _("kBuild-endef-%s is missing kBuild-define-%s"),
+ eval_kbuild_type_to_string(enmType), eval_kbuild_type_to_string(enmType));
+ return 0;
+ }
+
+ /*
+ * ... and does it have a matching kind?
+ */
+ pObj = pData->pObj;
+ if (pObj->enmType != enmType)
+ OSSS(error, pFileLoc, _("'kBuild-endef-%s' does not match 'kBuild-define-%s %s'"),
+ eval_kbuild_type_to_string(enmType), eval_kbuild_type_to_string(pObj->enmType), pObj->pszName);
+
+ /*
+ * The endef-kbuild may optionally be followed by the target name.
+ * It should match the name given to the kBuild-define.
+ */
+ pszName = allocate_expanded_next_token(&pszLine, pszEos, &cchName, 1 /*fStrip*/);
+ if (pszName)
+ {
+ if ( cchName != pObj->cchName
+ || strcmp(pszName, pObj->pszName))
+ OSSSS(error, pFileLoc, _("'kBuild-endef-%s %s' does not match 'kBuild-define-%s %s'"),
+ eval_kbuild_type_to_string(enmType), pszName,
+ eval_kbuild_type_to_string(pObj->enmType), pObj->pszName);
+ free(pszName);
+ }
+
+ /*
+ * Pop a define off the stack.
+ */
+ assert(pData == g_pTopKbEvalData);
+ *ppData = g_pTopKbEvalData = pData->pStackDown;
+ pData->pStackDown = NULL;
+ current_variable_set_list = pData->pVariablesSaved;
+ pData->pVariablesSaved = NULL;
+ free(pData);
+
+ return 0;
+}
+
+int eval_kbuild_read_hook(struct kbuild_eval_data **kdata, const floc *flocp,
+ const char *pchWord, size_t cchWord, const char *line, const char *eos, int ignoring)
+{
+ enum kBuildType enmType;
+
+ /*
+ * Skip the 'kBuild-' prefix that the caller already matched.
+ */
+ assert(memcmp(pchWord, "kBuild-", sizeof("kBuild-") - 1) == 0);
+ pchWord += sizeof("kBuild-") - 1;
+ cchWord -= sizeof("kBuild-") - 1;
+
+ /*
+ * String switch.
+ */
+ if ( cchWord >= sizeof("define-") - 1
+ && strneq(pchWord, "define-", sizeof("define-") - 1))
+ {
+ enmType = eval_kbuild_type_from_string(pchWord + sizeof("define-") - 1, cchWord - sizeof("define-") + 1);
+ if (enmType != kBuildType_Invalid)
+ return eval_kbuild_define_xxxx(kdata, flocp, line, eos, ignoring, enmType);
+ }
+ else if ( cchWord >= sizeof("endef-") - 1
+ && strneq(pchWord, "endef-", sizeof("endef-") - 1))
+ {
+ enmType = eval_kbuild_type_from_string(pchWord + sizeof("endif-") - 1, cchWord - sizeof("endif-") + 1);
+ if (enmType != kBuildType_Invalid)
+ return eval_kbuild_endef_xxxx(kdata, flocp, line, eos, ignoring, enmType);
+ }
+ else if (WORD_IS(pchWord, cchWord, "endef"))
+ {
+ /* Terminate whatever definition is on top. */
+
+ }
+
+ /*
+ * Everything that is prefixed with 'kBuild-' is reserved for language
+ * extensions, at least until legacy assignments/whatever turns up.
+ */
+ error(flocp, cchWord, _("Unknown syntax 'kBuild-%.*s'"), (int)cchWord, pchWord);
+ return 0;
+}
+
+
+/** @name kBuild object variable accessor related functions
+ * @{
+ */
+
+/**
+ * Checks if the given name is an object variable accessor.
+ *
+ * @returns 1 if it is, 0 if it isn't.
+ * @param pchName The potential kBuild variable accessor
+ * expression.
+ * @param cchName Length of the expression.
+ */
+int is_kbuild_object_variable_accessor(const char *pchName, size_t cchName)
+{
+ char const *pchTmp;
+
+ /* See lookup_kbuild_object_variable for the rules. */
+ if (cchName >= 1+1+1+1 && *pchName == '[')
+ {
+ pchName++;
+ cchName--;
+
+ pchTmp = memchr(pchName, '@', cchName);
+ if (pchTmp)
+ {
+ cchName -= pchTmp + 1 - pchName;
+ pchName = pchTmp + 1;
+ pchTmp = memchr(pchName, ']', cchName);
+ if (pchTmp)
+ {
+ cchName -= pchTmp + 1 - pchName;
+ if (cchName > 0)
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * Parses a kBuild object variable accessor, resolving the object.
+ *
+ * @returns Pointer to the variable if found.
+ * @retval NULL if the object (or type) couldn't be resolved.
+ * @retval KOBJ_NOT_KBUILD_ACCESSOR if no a kBuild variable accessor.
+ *
+ * @param pchExpr The kBuild variable accessor expression.
+ * @param cchExpr Length of the expression.
+ * @param enmSeverity The minimum severity level for errors.
+ * @param pFileLoc The file location any errors should be reported
+ * at. Optional.
+ * @param ppchVarNm Where to return the pointer to the start of the
+ * variable name within the string @a pchExpr
+ * points to. Mandatory.
+ * @param pcchVarNm Where to return the length of the variable name.
+ * Mandatory.
+ * @param penmType Where to return the object type. Optional.
+ */
+static struct kbuild_object *
+parse_kbuild_object_variable_accessor(const char *pchExpr, size_t cchExpr,
+ enum kBuildSeverity enmSeverity, const floc *pFileLoc,
+ const char **ppchVarNm, size_t *pcchVarNm, enum kBuildType *penmType)
+{
+ const char * const pchOrgExpr = pchExpr;
+ size_t const cchOrgExpr = cchExpr;
+ char const *pchTmp;
+
+ /*
+ * To accept this as an kBuild accessor, we require:
+ * 1. Open bracket.
+ * 2. At sign separating the type from the name.
+ * 3. Closing bracket.
+ * 4. At least one character following it.
+ */
+ if (cchExpr >= 1+1+1+1 && *pchExpr == '[')
+ {
+ pchExpr++;
+ cchExpr--;
+
+ pchTmp = memchr(pchExpr, '@', cchExpr);
+ if (pchTmp)
+ {
+ const char * const pchType = pchExpr;
+ size_t const cchType = pchTmp - pchExpr;
+
+ cchExpr -= cchType + 1;
+ pchExpr = pchTmp + 1;
+ pchTmp = memchr(pchExpr, ']', cchExpr);
+ if (pchTmp)
+ {
+ const char * const pchObjName = pchExpr;
+ size_t const cchObjName = pchTmp - pchExpr;
+
+ cchExpr -= cchObjName + 1;
+ pchExpr = pchTmp + 1;
+ if (cchExpr > 0)
+ {
+
+ /*
+ * It's an kBuild define variable accessor, alright.
+ */
+ *pcchVarNm = cchExpr;
+ *ppchVarNm = pchExpr;
+
+ /* Deal with known special accessors: [@self]VAR, [@super]VAR. */
+ if (cchType == 0)
+ {
+ int fSuper;
+
+ if (WORD_IS(pchObjName, cchObjName, "self"))
+ fSuper = 0;
+ else if (WORD_IS(pchObjName, cchObjName, "super"))
+ fSuper = 1;
+ else
+ {
+ kbuild_report_problem(MAX(enmSeverity, kBuildSeverity_Error), pFileLoc,
+ _("Invalid special kBuild object accessor: '%.*s'"),
+ (int)cchOrgExpr, pchOrgExpr);
+ if (penmType)
+ *penmType = kBuildType_Invalid;
+ return NULL;
+ }
+ if (g_pTopKbEvalData)
+ {
+ struct kbuild_object *pObj = g_pTopKbEvalData->pObj;
+ struct kbuild_object *pParent;
+
+ if (penmType)
+ *penmType = pObj->enmType;
+
+ if (!fSuper)
+ return pObj;
+
+ pParent = get_kbuild_object_parent(pObj, MAX(enmSeverity, kBuildSeverity_Error));
+ if (pParent)
+ return pParent;
+ }
+ else
+ kbuild_report_problem(MAX(enmSeverity, kBuildSeverity_Error), pFileLoc,
+ _("The '%.*s' accessor can only be used in the context of a kBuild object"),
+ (int)cchOrgExpr, pchOrgExpr);
+ if (penmType)
+ *penmType = kBuildType_Invalid;
+ }
+ else
+ {
+ /* Genric accessor. Check the type and look up the object. */
+ enum kBuildType enmType = eval_kbuild_type_from_string(pchType, cchType);
+ if (penmType)
+ *penmType = enmType;
+ if (enmType != kBuildType_Invalid)
+ {
+ struct kbuild_object *pObj = lookup_kbuild_object(enmType, pchObjName, cchObjName);
+ if (pObj)
+ return pObj;
+
+ /* failed. */
+ kbuild_report_problem(enmSeverity, pFileLoc,
+ _("kBuild object '%s' not found in kBuild variable accessor '%.*s'"),
+ (int)cchObjName, pchObjName, (int)cchOrgExpr, pchOrgExpr);
+ }
+ else
+ kbuild_report_problem(MAX(enmSeverity, kBuildSeverity_Error), pFileLoc,
+ _("Invalid type '%.*s' specified in kBuild variable accessor '%.*s'"),
+ (int)cchType, pchType, (int)cchOrgExpr, pchOrgExpr);
+ }
+ return NULL;
+ }
+ }
+ }
+ }
+
+ *ppchVarNm = NULL;
+ *pcchVarNm = 0;
+ if (penmType)
+ *penmType = kBuildType_Invalid;
+ return KOBJ_NOT_KBUILD_ACCESSOR;
+}
+
+/**
+ * Looks up a variable in a kBuild object.
+ *
+ * The caller has done minimal matching, i.e. starting square brackets and
+ * minimum length. We do the rest here.
+ *
+ * @returns Pointer to the variable if found.
+ * @retval NULL if not found.
+ * @retval VAR_NOT_KBUILD_ACCESSOR if no a kBuild variable accessor.
+ *
+ * @param pchName The kBuild variable accessor expression.
+ * @param cchName Length of the expression.
+ */
+struct variable *
+lookup_kbuild_object_variable_accessor(const char *pchName, size_t cchName)
+{
+ /*const char * const pchOrgName = pchName;*/
+ /*size_t const cchOrgName = cchName;*/
+ const char * pchVarNm;
+ size_t cchVarNm;
+ struct kbuild_object *pObj;
+
+ pObj = parse_kbuild_object_variable_accessor(pchName, cchName, kBuildSeverity_Warning, NULL, &pchVarNm, &cchVarNm, NULL);
+ if (pObj != KOBJ_NOT_KBUILD_ACCESSOR)
+ {
+ if (pObj)
+ {
+ /*
+ * Do the variable lookup.
+ */
+ const char *pszCachedName = strcache2_lookup(&variable_strcache, pchVarNm, cchVarNm);
+ if (pszCachedName)
+ {
+ struct variable VarKey;
+ struct variable *pVar;
+ VarKey.name = pszCachedName;
+ VarKey.length = cchName;
+
+ pVar = (struct variable *)hash_find_item_strcached(&pObj->pVariables->set->table, &VarKey);
+ if (pVar)
+ return pVar;
+
+ /*
+ * Not found, check ancestors if any.
+ */
+ if (pObj->pszParent || pObj->pszTemplate)
+ {
+ struct kbuild_object *pParent = pObj;
+ for (;;)
+ {
+ pParent = resolve_kbuild_object_parent(pParent, 0 /*fQuiet*/);
+ if (!pParent)
+ break;
+ pVar = (struct variable *)hash_find_item_strcached(&pParent->pVariables->set->table, &VarKey);
+ if (pVar)
+ return pVar;
+ }
+ }
+ }
+ }
+
+ /* Not found one way or the other. */
+ return NULL;
+ }
+
+ /* Not a kBuild object variable accessor. */
+ return VAR_NOT_KBUILD_ACCESSOR;
+}
+
+/** @} */
+
+void print_kbuild_data_base(void)
+{
+ struct kbuild_object *pCur;
+
+ puts(_("\n# kBuild defines"));
+
+ for (pCur = g_pHeadKbObjs; pCur; pCur = pCur->pGlobalNext)
+ {
+ printf("\nkBuild-define-%s %s",
+ eval_kbuild_type_to_string(pCur->enmType), pCur->pszName);
+ if (pCur->pszParent)
+ printf(" extending %s", pCur->pszParent);
+ if (pCur->pszTemplate)
+ printf(" using %s", pCur->pszTemplate);
+ putchar('\n');
+
+ print_variable_set(pCur->pVariables->set, "", 0);
+
+ printf("kBuild-endef-%s %s\n",
+ eval_kbuild_type_to_string(pCur->enmType), pCur->pszName);
+ }
+ /** @todo hash stats. */
+}
+
+void print_kbuild_define_stats(void)
+{
+ /* later when hashing stuff */
+}
+