/* $Id: kbuild-object.c 3141 2018-03-14 21:58:32Z bird $ */ /** @file * kBuild objects. */ /* * Copyright (c) 2011-2014 knut st. osmundsen * * 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 * */ /* 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 #include /******************************************************************************* * 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