/* $Id: kbuild-read.c 2549 2011-11-09 01:22:04Z bird $ */ /** @file * kBuild specific make functionality related to read.c. */ /* * Copyright (c) 2011 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 "make.h" #include "filedef.h" #include "variable.h" #include "dep.h" #include "debug.h" #include "kbuild.h" #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 * *******************************************************************************/ /** Indicate which kind of kBuild define we're working on. */ enum kBuildDef { kBuildDef_Invalid, kBuildDef_Target, kBuildDef_Template, kBuildDef_Tool, kBuildDef_Sdk, kBuildDef_Unit }; enum kBuildExtendBy { kBuildExtendBy_NoParent, kBuildExtendBy_Overriding, kBuildExtendBy_Appending, kBuildExtendBy_Prepending }; /** * The data we stack during eval. */ struct kbuild_eval_data { /** The kind of define. */ enum kBuildDef enmKind; /** The bare name of the define. */ char *pszName; /** The file location where this define was declared. */ struct floc FileLoc; /** Pointer to the next element in the global list. */ struct kbuild_eval_data *pGlobalNext; /** Pointer to the element below us on the stack. */ struct kbuild_eval_data *pStackDown; /** The variable set associated with this define. */ struct variable_set_list *pVariables; /** The saved current variable set, for restoring in kBuild-endef. */ struct variable_set_list *pVariablesSaved; /** The parent name, NULL if none. */ char *pszParent; /** The inheritance method. */ enum kBuildExtendBy enmExtendBy; /** Pointer to the parent. Resolved lazily, so it can be NULL even if we have * a parent. */ struct kbuild_eval_data *pParent; /** The template, NULL if none. Only applicable to targets. */ char *pszTemplate; /** Pointer to the template. Resolved lazily, so it can be NULL even if we have * a parent. */ struct kbuild_eval_data *pTemplate; /** The variable prefix. */ char *pszVarPrefix; /** The length of the variable prefix. */ size_t cchVarPrefix; }; /******************************************************************************* * Header Files * *******************************************************************************/ /** Linked list (LIFO) of kBuild defines. * @todo use a hash! */ struct kbuild_eval_data *g_pHeadKbDefs = NULL; /** Stack of kBuild defines. */ struct kbuild_eval_data *g_pTopKbDef = NULL; struct variable_set * get_top_kbuild_variable_set(void) { struct kbuild_eval_data *pTop = g_pTopKbDef; assert(pTop != NULL); return pTop->pVariables->set; } char * kbuild_prefix_variable(const char *pszName, unsigned int *pcchName) { struct kbuild_eval_data *pTop = g_pTopKbDef; char *pszPrefixed; unsigned int cchPrefixed; assert(pTop != NULL); cchPrefixed = pTop->cchVarPrefix + *pcchName; pszPrefixed = xmalloc(cchPrefixed + 1); memcpy(pszPrefixed, pTop->pszVarPrefix, pTop->cchVarPrefix); memcpy(&pszPrefixed[pTop->cchVarPrefix], pszName, *pcchName); pszPrefixed[cchPrefixed] = '\0'; *pcchName = cchPrefixed; return pszPrefixed; } static const char * eval_kbuild_kind_to_string(enum kBuildDef enmKind) { switch (enmKind) { case kBuildDef_Target: return "target"; case kBuildDef_Template: return "template"; case kBuildDef_Tool: return "tool"; case kBuildDef_Sdk: return "sdk"; case kBuildDef_Unit: return "unit"; default: case kBuildDef_Invalid: return "invalid"; } } static char * allocate_expanded_next_token(const char **ppszCursor, const char *pszEos, unsigned int *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; } static struct kbuild_eval_data * eval_kbuild_resolve_parent(struct kbuild_eval_data *pData) { if ( !pData->pParent && pData->pszParent) { struct kbuild_eval_data *pCur = g_pHeadKbDefs; while (pCur) { if ( pCur->enmKind == pData->enmKind && !strcmp(pCur->pszName, pData->pszParent)) { if ( pCur->pszParent && ( pCur->pParent == pData || !strcmp(pCur->pszParent, pData->pszName)) ) fatal(&pData->FileLoc, _("'%s' and '%s' are both trying to be each other children..."), pData->pszName, pCur->pszName); pData->pParent = pCur; pData->pVariables->next = pData->pVariables; break; } pCur = pCur->pGlobalNext; } } return pData->pParent; } static int eval_kbuild_define_xxxx(struct kbuild_eval_data **ppData, const struct floc *pFileLoc, const char *pszLine, const char *pszEos, int fIgnoring, enum kBuildDef enmKind) { unsigned int cch; unsigned int cchName; char ch; char *psz; const char *pszPrefix; struct kbuild_eval_data *pData; if (fIgnoring) return 0; /* * Create a new kBuild eval data item. */ pData = xmalloc(sizeof(*pData)); pData->enmKind = enmKind; pData->pszName = NULL; pData->FileLoc = *pFileLoc; pData->pGlobalNext = g_pHeadKbDefs; g_pHeadKbDefs = pData; pData->pStackDown = *ppData; *ppData = g_pTopKbDef = pData; pData->pVariables = create_new_variable_set(); pData->pVariablesSaved = NULL; pData->pszParent = NULL; pData->enmExtendBy = kBuildExtendBy_NoParent; pData->pParent = NULL; pData->pszTemplate = NULL; pData->pTemplate = NULL; pData->pszVarPrefix = NULL; pData->cchVarPrefix = 0; /* * The first word is the name. */ pData->pszName = allocate_expanded_next_token(&pszLine, pszEos, &cchName, 1 /*strip*/); if (!pData->pszName || !*pData->pszName) fatal(pFileLoc, _("The kBuild define requires a name")); psz = pData->pszName; while ((ch = *psz++) != '\0') if (!isgraph(ch)) { error(pFileLoc, _("The 'kBuild-define-%s' name '%s' contains one or more invalid characters"), eval_kbuild_kind_to_string(enmKind), pData->pszName); break; } /* * Parse subsequent words. */ psz = find_next_token_eos(&pszLine, pszEos, &cch); while (psz) { if (WORD_IS(psz, cch, "extending")) { /* Inheritance directive. */ if (pData->pszParent != NULL) fatal(pFileLoc, _("'extending' can only occure once")); pData->pszParent = allocate_expanded_next_token(&pszLine, pszEos, &cch, 1 /*strip*/); if (!pData->pszParent || !*pData->pszParent) fatal(pFileLoc, _("'extending' requires a parent name")); pData->enmExtendBy = kBuildExtendBy_Overriding; /* optionally 'by overriding|prepending|appending' */ psz = find_next_token_eos(&pszLine, pszEos, &cch); if (psz && WORD_IS(psz, cch, "by")) { cch = 0; psz = find_next_token_eos(&pszLine, pszEos, &cch); if (WORD_IS(psz, cch, "overriding")) pData->enmExtendBy = kBuildExtendBy_Overriding; else if (WORD_IS(psz, cch, "appending")) pData->enmExtendBy = kBuildExtendBy_Appending; else if (WORD_IS(psz, cch, "prepending")) pData->enmExtendBy = kBuildExtendBy_Prepending; else fatal(pFileLoc, _("Unknown 'extending by' method '%.*s'"), (int)cch, psz); /* next token */ psz = find_next_token_eos(&pszLine, pszEos, &cch); } } else if (WORD_IS(psz, cch, "using")) { /* Template directive. */ if (enmKind != kBuildDef_Tool) fatal(pFileLoc, _("'using