/* $Id: kbuild.c 3425 2020-08-21 12:45:06Z bird $ */ /** @file * kBuild specific make functionality. */ /* * Copyright (c) 2006-2010 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 * *******************************************************************************/ #define NO_MEMCOPY_HACK #include "makeint.h" #include "filedef.h" #include "variable.h" #include "dep.h" #include "debug.h" #ifdef WINDOWS32 # include "pathstuff.h" # include #endif #if defined(__APPLE__) # include #endif #if defined(__FreeBSD__) # include # include #endif #include "kbuild.h" #include "k/kDefs.h" #include /******************************************************************************* * Defined Constants And Macros * *******************************************************************************/ /** Helper for passing a string constant to kbuild_get_variable_n. */ #define ST(strconst) strconst, sizeof(strconst) - 1 #if 1 # define my_memcpy(dst, src, len) \ do { \ if (len > 8) \ memcpy(dst, src, len); \ else \ switch (len) \ { \ case 8: dst[7] = src[7]; /* fall thru */ \ case 7: dst[6] = src[6]; /* fall thru */ \ case 6: dst[5] = src[5]; /* fall thru */ \ case 5: dst[4] = src[4]; /* fall thru */ \ case 4: dst[3] = src[3]; /* fall thru */ \ case 3: dst[2] = src[2]; /* fall thru */ \ case 2: dst[1] = src[1]; /* fall thru */ \ case 1: dst[0] = src[0]; /* fall thru */ \ case 0: break; \ } \ } while (0) #elif defined(__GNUC__) # define my_memcpy __builtin_memcpy #elif defined(_MSC_VER) # pragma instrinic(memcpy) # define my_memcpy memcpy #endif /******************************************************************************* * Global Variables * *******************************************************************************/ /** The argv[0] passed to main. */ static const char *g_pszExeName; /** The initial working directory. */ static char *g_pszInitialCwd; /** * Initialize kBuild stuff. * * @param argc Number of arguments to main(). * @param argv The main() argument vector. */ void init_kbuild(int argc, char **argv) { int rc; PATH_VAR(szTmp); /* * Get the initial cwd for use in my_abspath. */ #ifdef WINDOWS32 if (getcwd_fs(szTmp, GET_PATH_MAX) != 0) #else if (getcwd(szTmp, GET_PATH_MAX) != 0) #endif g_pszInitialCwd = xstrdup(szTmp); else O(fatal, NILF, _("getcwd failed")); /* * Determin the executable name. */ rc = -1; #if defined(__APPLE__) { const char *pszImageName = _dyld_get_image_name(0); if (pszImageName) { size_t cchImageName = strlen(pszImageName); if (cchImageName < GET_PATH_MAX) { memcpy(szTmp, pszImageName, cchImageName + 1); rc = 0; } } } #elif defined(__FreeBSD__) rc = readlink("/proc/curproc/file", szTmp, GET_PATH_MAX - 1); if (rc < 0 || rc == GET_PATH_MAX - 1) { rc = -1; # if 0 /* doesn't work because l_name isn't always absolute, it's just argv0 from exec or something. */ /* /proc is optional, try rtdl. */ void *hExe = dlopen(NULL, 0); rc = -1; if (hExe) { struct link_map const *pLinkMap = 0; if (dlinfo(hExe, RTLD_DI_LINKMAP, &pLinkMap) == 0) { const char *pszImageName = pLinkMap->l_name; size_t cchImageName = strlen(pszImageName); if (cchImageName < GET_PATH_MAX) { memcpy(szTmp, pszImageName, cchImageName + 1); rc = 0; } } } # endif } else szTmp[rc] = '\0'; #elif defined(__gnu_linux__) || defined(__linux__) rc = readlink("/proc/self/exe", szTmp, GET_PATH_MAX - 1); if (rc < 0 || rc == GET_PATH_MAX - 1) rc = -1; else szTmp[rc] = '\0'; #elif defined(__OS2__) _execname(szTmp, GET_PATH_MAX); rc = 0; #elif defined(__sun__) { char szTmp2[64]; snprintf(szTmp2, sizeof(szTmp2), "/proc/%ld/path/a.out", (long)getpid()); rc = readlink(szTmp2, szTmp, GET_PATH_MAX - 1); if (rc < 0 || rc == GET_PATH_MAX - 1) rc = -1; else szTmp[rc] = '\0'; } #elif defined(WINDOWS32) if (GetModuleFileName(GetModuleHandle(NULL), szTmp, GET_PATH_MAX)) rc = 0; #endif #if !defined(__OS2__) && !defined(WINDOWS32) /* fallback, try use the path to locate the binary. */ if ( rc < 0 && access(argv[0], X_OK)) { size_t cchArgv0 = strlen(argv[0]); const char *pszPath = getenv("PATH"); char *pszCopy = xstrdup(pszPath ? pszPath : "."); char *psz = pszCopy; while (*psz) { size_t cch; char *pszEnd = strchr(psz, PATH_SEPARATOR_CHAR); if (!pszEnd) pszEnd = strchr(psz, '\0'); cch = pszEnd - psz; if (cch + cchArgv0 + 2 <= GET_PATH_MAX) { memcpy(szTmp, psz, cch); szTmp[cch] = '/'; memcpy(&szTmp[cch + 1], argv[0], cchArgv0 + 1); if (!access(szTmp, X_OK)) { rc = 0; break; } } /* next */ psz = pszEnd; while (*psz == PATH_SEPARATOR_CHAR) psz++; } free(pszCopy); } #endif if (rc < 0) g_pszExeName = argv[0]; else g_pszExeName = xstrdup(szTmp); (void)argc; } /** * Wrapper that ensures correct starting_directory. */ static char *my_abspath(const char *pszIn, char *pszOut) { char *pszSaved, *pszRet; pszSaved = starting_directory; starting_directory = g_pszInitialCwd; pszRet = abspath(pszIn, pszOut); starting_directory = pszSaved; return pszRet; } /** * Determin the KBUILD_PATH value. * * @returns Pointer to static a buffer containing the value (consider it read-only). */ const char *get_kbuild_path(void) { static const char *s_pszPath = NULL; if (!s_pszPath) { PATH_VAR(szTmpPath); const char *pszEnvVar = getenv("KBUILD_PATH"); if ( !pszEnvVar || !my_abspath(pszEnvVar, szTmpPath)) { pszEnvVar = getenv("PATH_KBUILD"); if ( !pszEnvVar || !my_abspath(pszEnvVar, szTmpPath)) { #ifdef KBUILD_PATH return s_pszPath = KBUILD_PATH; #else /* $(abspath $(KBUILD_BIN_PATH)/../..)*/ size_t cch = strlen(get_kbuild_bin_path()); char *pszTmp2 = alloca(cch + sizeof("/../..")); strcat(strcpy(pszTmp2, get_kbuild_bin_path()), "/../.."); if (!my_abspath(pszTmp2, szTmpPath)) O(fatal, NILF, _("failed to determin KBUILD_PATH")); #endif } } s_pszPath = xstrdup(szTmpPath); } return s_pszPath; } /** * Determin the KBUILD_BIN_PATH value. * * @returns Pointer to static a buffer containing the value (consider it read-only). */ const char *get_kbuild_bin_path(void) { static const char *s_pszPath = NULL; if (!s_pszPath) { PATH_VAR(szTmpPath); const char *pszEnvVar = getenv("KBUILD_BIN_PATH"); if ( !pszEnvVar || !my_abspath(pszEnvVar, szTmpPath)) { pszEnvVar = getenv("PATH_KBUILD_BIN"); if ( !pszEnvVar || !my_abspath(pszEnvVar, szTmpPath)) { #ifdef KBUILD_PATH return s_pszPath = KBUILD_BIN_PATH; #else /* $(abspath $(dir $(ARGV0)).) */ size_t cch = strlen(g_pszExeName); char *pszTmp2 = alloca(cch + sizeof(".")); char *pszSep = pszTmp2 + cch - 1; memcpy(pszTmp2, g_pszExeName, cch); # ifdef HAVE_DOS_PATHS while (pszSep >= pszTmp2 && *pszSep != '/' && *pszSep != '\\' && *pszSep != ':') # else while (pszSep >= pszTmp2 && *pszSep != '/') # endif pszSep--; if (pszSep >= pszTmp2) strcpy(pszSep + 1, "."); else strcpy(pszTmp2, "."); if (!my_abspath(pszTmp2, szTmpPath)) OSS(fatal, NILF, _("failed to determin KBUILD_BIN_PATH (pszTmp2=%s szTmpPath=%s)"), pszTmp2, szTmpPath); #endif /* !KBUILD_PATH */ } } s_pszPath = xstrdup(szTmpPath); } return s_pszPath; } /** * Determin the location of default kBuild shell. * * @returns Pointer to static a buffer containing the location (consider it read-only). */ const char *get_default_kbuild_shell(void) { static char *s_pszDefaultShell = NULL; if (!s_pszDefaultShell) { #if defined(__OS2__) || defined(_WIN32) || defined(WINDOWS32) static const char s_szShellName[] = "/kmk_ash.exe"; #else static const char s_szShellName[] = "/kmk_ash"; #endif const char *pszBin = get_kbuild_bin_path(); size_t cchBin = strlen(pszBin); s_pszDefaultShell = xmalloc(cchBin + sizeof(s_szShellName)); memcpy(s_pszDefaultShell, pszBin, cchBin); memcpy(&s_pszDefaultShell[cchBin], s_szShellName, sizeof(s_szShellName)); } return s_pszDefaultShell; } #ifdef KMK_HELPERS /** * Applies the specified default path to any relative paths in *ppsz. * * @param pDefPath The default path. * @param ppsz Pointer to the string pointer. If we expand anything, *ppsz * will be replaced and the caller is responsible for calling free() on it. * @param pcch IN: *pcch contains the current string length. * OUT: *pcch contains the new string length. * @param pcchAlloc *pcchAlloc contains the length allocated for the string. Can be NULL. * @param fCanFree Whether *ppsz should be freed when we replace it. */ static void kbuild_apply_defpath(struct variable *pDefPath, char **ppsz, unsigned int *pcch, unsigned int *pcchAlloc, int fCanFree) { unsigned int cchInCur; unsigned int cchMaxRelative = 0; const char *pszInCur; /* * The first pass, count the relative paths. */ const char *pszIterator = *ppsz; const char * const pszEos = pszIterator + *pcch; unsigned int cRelativePaths = 0; assert(*pszEos == '\0'); while ((pszInCur = find_next_file_token(&pszIterator, pszEos, &cchInCur)) != NULL) { /* is relative? */ #ifdef HAVE_DOS_PATHS if (pszInCur[0] != '/' && pszInCur[0] != '\\' && (cchInCur < 2 || pszInCur[1] != ':')) #else if (pszInCur[0] != '/') #endif { cRelativePaths++; if (cchInCur > cchMaxRelative) cchMaxRelative = cchInCur; } } /* * The second pass construct the new string. */ if (cRelativePaths) { size_t const cchAbsPathBuf = MAX(GET_PATH_MAX, pDefPath->value_length + cchInCur + 1 + 16); char *pszAbsPathOut = (char *)alloca(cchAbsPathBuf); char *pszAbsPathIn = (char *)alloca(cchAbsPathBuf); size_t cchAbsDefPath; size_t cchOut; char *pszOut; char *pszOutCur; const char *pszInNextCopy = *ppsz; /* make defpath absolute and have a trailing slash first. */ if (abspath(pDefPath->value, pszAbsPathIn) == NULL) memcpy(pszAbsPathIn, pDefPath->value, pDefPath->value_length); cchAbsDefPath = strlen(pszAbsPathIn); #ifdef HAVE_DOS_PATHS if (pszAbsPathIn[cchAbsDefPath - 1] != '/' && pszAbsPathIn[cchAbsDefPath - 1] != '\\') #else if (pszAbsPathIn[cchAbsDefPath - 1] != '/') #endif pszAbsPathIn[cchAbsDefPath++] = '/'; cchOut = *pcch + cRelativePaths * cchAbsDefPath + 1; pszOutCur = pszOut = xmalloc(cchOut); cRelativePaths = 0; pszIterator = *ppsz; while ((pszInCur = find_next_file_token(&pszIterator, pszEos, &cchInCur))) { /* is relative? */ #ifdef HAVE_DOS_PATHS if (pszInCur[0] != '/' && pszInCur[0] != '\\' && (cchInCur < 2 || pszInCur[1] != ':')) #else if (pszInCur[0] != '/') #endif { const char *pszToCopy; size_t cchToCopy; /* Create the abspath input. */ memcpy(&pszAbsPathIn[cchAbsDefPath], pszInCur, cchInCur); pszAbsPathIn[cchAbsDefPath + cchInCur] = '\0'; pszToCopy = abspath(pszAbsPathIn, pszAbsPathOut); if (!pszToCopy) pszToCopy = pszAbsPathIn; /* copy leading input */ if (pszInCur != pszInNextCopy) { const size_t cchCopy = pszInCur - pszInNextCopy; memcpy(pszOutCur, pszInNextCopy, cchCopy); pszOutCur += cchCopy; } pszInNextCopy = pszInCur + cchInCur; /* copy out the abspath. */ cchToCopy = strlen(pszToCopy); assert(cchToCopy <= cchAbsDefPath + cchInCur); memcpy(pszOutCur, pszToCopy, cchToCopy); pszOutCur += cchToCopy; } /* else: Copy absolute paths as bulk when we hit then next relative one or the end. */ } /* the final copy (includes the nil). */ cchInCur = *ppsz + *pcch - pszInNextCopy; memcpy(pszOutCur, pszInNextCopy, cchInCur); pszOutCur += cchInCur; *pszOutCur = '\0'; assert((size_t)(pszOutCur - pszOut) < cchOut); /* set return values */ if (fCanFree) free(*ppsz); *ppsz = pszOut; *pcch = pszOutCur - pszOut; if (pcchAlloc) *pcchAlloc = cchOut; } } /** * Gets a variable that must exist. * Will cause a fatal failure if the variable doesn't exist. * * @returns Pointer to the variable. * @param pszName The variable name. * @param cchName The name length. */ MY_INLINE struct variable * kbuild_get_variable_n(const char *pszName, size_t cchName) { struct variable *pVar = lookup_variable(pszName, cchName); if (!pVar) fatal(NILF, cchName, _("variable `%.*s' isn't defined!"), (int)cchName, pszName); if (pVar->recursive) fatal(NILF, cchName, _("variable `%.*s' is defined as `recursive' instead of `simple'!"), (int)cchName, pszName); MY_ASSERT_MSG(strlen(pVar->value) == pVar->value_length, ("%u != %u %.*s\n", pVar->value_length, (unsigned int)strlen(pVar->value), (int)cchName, pVar->name)); return pVar; } /** * Gets a variable that must exist and can be recursive. * Will cause a fatal failure if the variable doesn't exist. * * @returns Pointer to the variable. * @param pszName The variable name. */ static struct variable * kbuild_get_recursive_variable(const char *pszName) { struct variable *pVar = lookup_variable(pszName, strlen(pszName)); if (!pVar) OS(fatal, NILF, _("variable `%s' isn't defined!"), pszName); MY_ASSERT_MSG(strlen(pVar->value) == pVar->value_length, ("%u != %u %s\n", pVar->value_length, (unsigned int)strlen(pVar->value), pVar->name)); return pVar; } /** * Gets a variable that doesn't have to exit, but if it does can be recursive. * * @returns Pointer to the variable. * NULL if not found. * @param pszName The variable name. Doesn't need to be terminated. * @param cchName The name length. */ static struct variable * kbuild_query_recursive_variable_n(const char *pszName, size_t cchName) { struct variable *pVar = lookup_variable(pszName, cchName); MY_ASSERT_MSG(!pVar || strlen(pVar->value) == pVar->value_length, ("%u != %u %.*s\n", pVar->value_length, (unsigned int)strlen(pVar->value), (int)cchName, pVar->name)); return pVar; } /** * Gets a variable that doesn't have to exit, but if it does can be recursive. * * @returns Pointer to the variable. * NULL if not found. * @param pszName The variable name. */ static struct variable * kbuild_query_recursive_variable(const char *pszName) { return kbuild_query_recursive_variable_n(pszName, strlen(pszName)); } /** * Converts the specified variable into a 'simple' one. * @returns pVar. * @param pVar The variable. */ static struct variable * kbuild_simplify_variable(struct variable *pVar) { if (memchr(pVar->value, '$', pVar->value_length)) { unsigned int value_len; char *pszExpanded = allocated_variable_expand_2(pVar->value, pVar->value_length, &value_len); #ifdef CONFIG_WITH_RDONLY_VARIABLE_VALUE if (pVar->rdonly_val) pVar->rdonly_val = 0; else #endif free(pVar->value); assert(pVar->origin != o_automatic); pVar->value = pszExpanded; pVar->value_length = value_len; pVar->value_alloc_len = value_len + 1; } pVar->recursive = 0; VARIABLE_CHANGED(pVar); return pVar; } /** * Looks up a variable. * The value_length field is valid upon successful return. * * @returns Pointer to the variable. NULL if not found. * @param pszName The variable name. * @param cchName The name length. */ MY_INLINE struct variable * kbuild_lookup_variable_n(const char *pszName, size_t cchName) { struct variable *pVar = lookup_variable(pszName, cchName); if (pVar) { MY_ASSERT_MSG(strlen(pVar->value) == pVar->value_length, ("%u != %u %.*s\n", pVar->value_length, (unsigned int)strlen(pVar->value), (int)cchName, pVar->name)); /* Make sure the variable is simple, convert it if necessary. Note! Must NOT do this for the dynamic INCS of sdks/ReorderCompilerIncs.kmk */ if (!pVar->recursive) { /* likely */ } else if ( cchName < sizeof("SDK_ReorderCompilerIncs_INCS.") - 1U || pszName[0] != 'S' || pszName[4] != 'R' || memcmp(pszName, "SDK_ReorderCompilerIncs_INCS.", sizeof("SDK_ReorderCompilerIncs_INCS.") - 1U) == 0) kbuild_simplify_variable(pVar); } return pVar; } /** * Looks up an non-empty variable when simplified and spaces skipped. * * This handy when emulating $(firstword )/$(lastword ) behaviour. * * @returns Pointer to the variable. NULL if not found. * @param pszName The variable name. * @param cchName The name length. */ MY_INLINE struct variable * kbuild_lookup_not_empty_variable_n(const char *pszName, size_t cchName) { struct variable *pVar = kbuild_lookup_variable_n(pszName, cchName); if (pVar && !pVar->recursive) { /* * Skip spaces and make sure it's non-zero. */ char *psz = pVar->value; if (!ISSPACE(*psz)) { /* kind of likely */ } else do psz++; while (ISSPACE(*psz)); if (*psz) { /*kind of likely */ } else pVar = NULL; } return pVar; } /** * Looks up a variable. * The value_length field is valid upon successful return. * * @returns Pointer to the variable. NULL if not found. * @param pszName The variable name. */ MY_INLINE struct variable * kbuild_lookup_variable(const char *pszName) { return kbuild_lookup_variable_n(pszName, strlen(pszName)); } /** * Looks up a variable and applies default a path to all relative paths. * The value_length field is valid upon successful return. * * @returns Pointer to the variable. NULL if not found. * @param pDefPath The default path. * @param pszName The variable name. * @param cchName The name length. */ MY_INLINE struct variable * kbuild_lookup_variable_defpath_n(struct variable *pDefPath, const char *pszName, size_t cchName) { struct variable *pVar = kbuild_lookup_variable_n(pszName, cchName); if (pVar && pDefPath) { assert(pVar->origin != o_automatic); #ifdef CONFIG_WITH_RDONLY_VARIABLE_VALUE assert(!pVar->rdonly_val); #endif kbuild_apply_defpath(pDefPath, &pVar->value, &pVar->value_length, &pVar->value_alloc_len, 1); } return pVar; } /** * Looks up a variable and applies default a path to all relative paths. * The value_length field is valid upon successful return. * * @returns Pointer to the variable. NULL if not found. * @param pDefPath The default path. * @param pszName The variable name. */ MY_INLINE struct variable * kbuild_lookup_variable_defpath(struct variable *pDefPath, const char *pszName) { struct variable *pVar = kbuild_lookup_variable(pszName); if (pVar && pDefPath) { assert(pVar->origin != o_automatic); #ifdef CONFIG_WITH_RDONLY_VARIABLE_VALUE assert(!pVar->rdonly_val); #endif kbuild_apply_defpath(pDefPath, &pVar->value, &pVar->value_length, &pVar->value_alloc_len, 1); } return pVar; } /** * Gets the first defined property variable. * * When pBldType is given, additional property variations are consulted. * See _TARGET_TOOL/r2433 in footer.kmk for the target-level difference. * Similar extended property lookup is applied to the source part if the * fExtendedSource parameter is non-zero (not done yet as it'll be expensive). * * Since r3415 this function will use $(target)_2_$(type)TOOL to cache the * result of the target specific part of the lookup (_TARGET_TOOL). */ static struct variable * kbuild_first_prop(struct variable *pTarget, struct variable *pSource, struct variable *pTool, struct variable *pType, struct variable *pBldTrg, struct variable *pBldTrgArch, struct variable *pBldType, const char *pszPropF1, char cchPropF1, const char *pszPropF2, char cchPropF2, int fExtendedSource, const char *pszVarName) { struct variable *pVar; size_t cchBuf; char *pszBuf; char *psz, *psz1, *psz2, *psz3, *psz4, *pszEnd; int fCacheIt = 1; fExtendedSource = fExtendedSource && pBldType != NULL; /* calc and allocate a too big name buffer. */ cchBuf = cchPropF2 + 1 + cchPropF1 + 1 + pTarget->value_length + 1 + pSource->value_length + 1 + (pTool ? pTool->value_length + 1 : 0) + pType->value_length + 1 + pBldTrg->value_length + 1 + pBldTrgArch->value_length + 1 + (pBldType ? pBldType->value_length + 1 : 0); pszBuf = xmalloc(cchBuf); #define ADD_VAR(pVar) do { my_memcpy(psz, (pVar)->value, (pVar)->value_length); psz += (pVar)->value_length; } while (0) #define ADD_STR(pszStr, cchStr) do { my_memcpy(psz, (pszStr), (cchStr)); psz += (cchStr); } while (0) #define ADD_CSTR(pszStr) do { my_memcpy(psz, pszStr, sizeof(pszStr) - 1); psz += sizeof(pszStr) - 1; } while (0) #define ADD_CH(ch) do { *psz++ = (ch); } while (0) /* * $(target)_$(source)_$(type)$(propf2).$(bld_trg).$(bld_trg_arch).$(bld_type) * ... * $(target)_$(source)_$(type)$(propf2) */ psz = pszBuf; ADD_VAR(pTarget); ADD_CH('_'); ADD_VAR(pSource); ADD_CH('_'); psz1 = psz; ADD_VAR(pType); ADD_STR(pszPropF2, cchPropF2); #define DO_VARIATIONS(a_fExtended) do { \ psz2 = psz; \ ADD_CH('.'); \ ADD_VAR(pBldTrg); \ psz3 = psz; \ ADD_CH('.'); \ ADD_VAR(pBldTrgArch); \ psz4 = psz; \ if ((a_fExtended) && pBldType) \ { \ ADD_CH('.'); \ ADD_VAR(pBldType); \ pVar = kbuild_lookup_not_empty_variable_n(pszBuf, psz - pszBuf); \ \ /* .$(bld_trg).$(bld_trg_arch) */ \ if (!pVar) \ pVar = kbuild_lookup_not_empty_variable_n(pszBuf, psz4 - pszBuf); \ \ /* .$(bld_trg).$(bld_type) */ \ if (!pVar) \ { \ psz = psz3 + 1; \ ADD_VAR(pBldType); \ pVar = kbuild_lookup_not_empty_variable_n(pszBuf, psz - pszBuf); \ } \ \ /* .$(bld_trg_arch) */ \ if (!pVar) \ { \ psz = psz2 + 1; \ ADD_VAR(pBldTrgArch); \ pVar = kbuild_lookup_not_empty_variable_n(pszBuf, psz - pszBuf); \ } \ \ /* .$(bld_trg) */ \ if (!pVar) \ { \ psz = psz2 + 1; \ ADD_VAR(pBldTrg); \ pVar = kbuild_lookup_not_empty_variable_n(pszBuf, psz - pszBuf); \ } \ \ /* .$(bld_type) */ \ if (!pVar) \ { \ psz = psz2 + 1; \ ADD_VAR(pBldType); \ pVar = kbuild_lookup_not_empty_variable_n(pszBuf, psz - pszBuf); \ } \ } \ else \ { \ /* .$(bld_trg).$(bld_trg_arch) */ \ pVar = kbuild_lookup_not_empty_variable_n(pszBuf, psz4 - pszBuf); \ \ /* .$(bld_trg) */ \ if (!pVar) \ pVar = kbuild_lookup_not_empty_variable_n(pszBuf, psz3 - pszBuf); \ } \ \ /* */ \ if (!pVar) \ pVar = kbuild_lookup_not_empty_variable_n(pszBuf, psz2 - pszBuf); \ } while (0) DO_VARIATIONS(fExtendedSource); /* * $(target)_$(source)_$(propf2).$(bld_trg).$(bld_trg_arch).$(bld_type) [omit $(type) prefix to $(propf2)] * ... * $(target)_$(source)_$(propf2) */ if (!pVar) { psz = psz1; /* rewind to '$(target)_$(source)_' */ ADD_STR(pszPropF2, cchPropF2); DO_VARIATIONS(fExtendedSource); } /* * $(source)_$(type)$(propf2).$(bld_trg).$(bld_trg_arch).$(bld_type) * ... * $(source)_$(type)$(propf2) */ if (!pVar) { psz = pszBuf; ADD_VAR(pSource); ADD_CH('_'); psz1 = psz; ADD_VAR(pType); ADD_STR(pszPropF2, cchPropF2); DO_VARIATIONS(fExtendedSource); /* * $(source)_$(propf2).$(bld_trg).$(bld_trg_arch).$(bld_type) [omit $(type) prefix to $(propf2)] * ... * $(source)_$(propf2) */ if (!pVar) { psz = psz1; /* rewind to '$(source)_' */ ADD_STR(pszPropF2, cchPropF2); DO_VARIATIONS(fExtendedSource); } } /* * Check the cache: $(target)_2_$(type)$(propf2) */ if (pVar) fCacheIt = 0; else if (fCacheIt) { psz = pszBuf; ADD_VAR(pTarget); ADD_STR("_2_", 3); ADD_VAR(pType); ADD_STR(pszPropF2, cchPropF2); pVar = kbuild_lookup_variable_n(pszBuf, psz - pszBuf); /* If found, this can be duplicated and returned directly. No value stripping needed as we defined it (or at least should have) ourselves. */ if (pVar) { pVar = define_variable_vl(pszVarName, strlen(pszVarName), pVar->value, pVar->value_length, 1 /* duplicate */, o_local, 0 /* !recursive */); free(pszBuf); return pVar; } } /* * $(target)_$(type)$(propf2).$(bld_trg).$(bld_trg_arch).$(bld_type) * ... * $(target)_$(type)$(propf2) */ if (!pVar) { psz = pszBuf; ADD_VAR(pTarget); ADD_CH('_'); psz1 = psz; ADD_VAR(pType); ADD_STR(pszPropF2, cchPropF2); DO_VARIATIONS(1); /* * $(target)_$(propf2).$(bld_trg).$(bld_trg_arch).$(bld_type) [omit $(type) prefix to $(propf2)] * ... * $(target)_$(propf2) */ if (!pVar) { psz = psz1; /* rewind to '$(target)_' */ ADD_STR(pszPropF2, cchPropF2); DO_VARIATIONS(1); } } /* * TOOL_$(tool)_$(type)$(propf2).$(bld_trg).$(bld_trg_arch).$(bld_type) * ... * TOOL_$(tool)_$(type)$(propf2) */ if (!pVar && pTool) { psz = pszBuf; ADD_CSTR("TOOL_"); ADD_VAR(pTool); ADD_CH('_'); psz1 = psz; ADD_VAR(pType); ADD_STR(pszPropF2, cchPropF2); DO_VARIATIONS(1); /* * TOOL_$(tool)_$(propf2).$(bld_trg).$(bld_trg_arch).$(bld_type) [omit $(type) prefix to $(propf2)] * ... * TOOL_$(tool)_$(propf2) */ if (!pVar) { psz = psz1; /* rewind to 'TOOL_$(tool)_' */ ADD_STR(pszPropF2, cchPropF2); DO_VARIATIONS(1); } } /* * $(type)$(propf1).$(bld_trg).$(bld_trg_arch).$(bld_type) * ... * $(type)$(propf1) */ if (!pVar) { psz = pszBuf; ADD_VAR(pType); ADD_STR(pszPropF1, cchPropF1); DO_VARIATIONS(1); /* * $(propf1).$(bld_trg).$(bld_trg_arch).$(bld_type) * ... * $(propf1) */ if (!pVar) { psz = pszBuf; ADD_STR(pszPropF1, cchPropF1); DO_VARIATIONS(1); } } /* * Done! */ if (pVar) { /* strip it */ psz = pVar->value; pszEnd = psz + pVar->value_length; while (ISBLANK(*psz)) psz++; while (pszEnd > psz && ISBLANK(pszEnd[-1])) pszEnd--; if (pszEnd > psz) { char chSaved = *pszEnd; *pszEnd = '\0'; pVar = define_variable_vl(pszVarName, strlen(pszVarName), psz, pszEnd - psz, 1 /* duplicate */, o_local, 0 /* !recursive */); *pszEnd = chSaved; } else pVar = NULL; } /* Cache the result if needed. */ if (fCacheIt) { psz = pszBuf; ADD_VAR(pTarget); ADD_STR("_2_", 3); ADD_VAR(pType); ADD_STR(pszPropF2, cchPropF2); define_variable_vl_global(pszBuf, psz - pszBuf, pVar ? pVar->value : "", pVar ? pVar->value_length : 0, 1 /* duplicate */, o_file, 0 /* !recursive */, NILF); } #undef ADD_VAR #undef ADD_STR #undef ADD_CSTR #undef ADD_CH free(pszBuf); return pVar; } /* * _SOURCE_TOOL = $(strip $(firstword \ $($(target)_$(source)_$(type)TOOL.$(bld_trg).$(bld_trg_arch)) \ $($(target)_$(source)_$(type)TOOL.$(bld_trg)) \ $($(target)_$(source)_$(type)TOOL) \ $($(target)_$(source)_TOOL.$(bld_trg).$(bld_trg_arch)) \ $($(target)_$(source)_TOOL.$(bld_trg)) \ $($(target)_$(source)_TOOL) \ $($(source)_$(type)TOOL.$(bld_trg).$(bld_trg_arch)) \ $($(source)_$(type)TOOL.$(bld_trg)) \ $($(source)_$(type)TOOL) \ $($(source)_TOOL.$(bld_trg).$(bld_trg_arch)) \ $($(source)_TOOL.$(bld_trg)) \ $($(source)_TOOL) \ $($(target)_$(type)TOOL.$(bld_trg).$(bld_trg_arch)) \ $($(target)_$(type)TOOL.$(bld_trg)) \ $($(target)_$(type)TOOL) \ $($(target)_TOOL.$(bld_trg).$(bld_trg_arch)) \ $($(target)_TOOL.$(bld_trg)) \ $($(target)_TOOL) \ \ - the rest depends on pBldType, see _TARGET_TOOL and kbuild_first_prop. $($(type)TOOL.$(bld_trg).$(bld_trg_arch)) \ $($(type)TOOL.$(bld_trg)) \ $($(type)TOOL) \ $(TOOL.$(bld_trg).$(bld_trg_arch)) \ $(TOOL.$(bld_trg)) \ $(TOOL) )) */ static struct variable * kbuild_get_source_tool(struct variable *pTarget, struct variable *pSource, struct variable *pType, struct variable *pBldTrg, struct variable *pBldTrgArch, struct variable *pBldType, const char *pszVarName) { struct variable *pVar = kbuild_first_prop(pTarget, pSource, NULL, pType, pBldTrg, pBldTrgArch, pBldType, "TOOL", sizeof("TOOL") - 1, "TOOL", sizeof("TOOL") - 1, 0 /*fExtendedSource*/, pszVarName); if (!pVar) OSS(fatal, NILF, _("no tool for source `%s' in target `%s'!"), pSource->value, pTarget->value); return pVar; } /** * Helper for func_kbuild_source_tool, func_kbuild_source_one, ++. */ static int kbuild_version_to_int(const char *pszVersion, int fStrict) { int iVer = 0; if (pszVersion && pszVersion[0]) { switch (pszVersion[0] | (pszVersion[1] << 8)) { case '2': iVer = 2; break; case '3': iVer = 3; break; case '4': iVer = 4; break; case '5': iVer = 5; break; case '6': iVer = 6; break; case '7': iVer = 7; break; case '8': iVer = 8; break; case '9': iVer = 9; break; case '0': iVer = 0; break; case '1': iVer = 1; break; default: while (ISBLANK(*pszVersion)) pszVersion++; if (*pszVersion) { char *pszEnd = NULL; long lVer; errno = 0; lVer = strtol(pszVersion, &pszEnd, 10); iVer = (int)lVer; if (fStrict) { if (lVer == 0 && errno != 0) OSN(fatal, NILF, _("invalid version argument '%s': errno=%d"), pszVersion, errno); else if (iVer != (int)lVer || iVer < 0) OS(fatal, NILF, _("version argument out of range '%s'"), pszVersion); else if (pszEnd) { while (ISBLANK(*pszEnd)) pszEnd++; if (*pszEnd) OS(fatal, NILF, _("version is not numerical '%s'"), pszVersion); } } } break; } } return iVer; } /** * "kb-src-tool [ver=0]" - implements _SOURCE_TOOL. * * Since r3415 an extended set of keyword variations is used on the target, tool * and global properties. */ char * func_kbuild_source_tool(char *o, char **argv, const char *pszFuncName) { const int iVer = kbuild_version_to_int(argv[1], 1 /*strict*/); struct variable *pVar = kbuild_get_source_tool(kbuild_get_variable_n(ST("target")), kbuild_get_variable_n(ST("source")), kbuild_get_variable_n(ST("type")), kbuild_get_variable_n(ST("bld_trg")), kbuild_get_variable_n(ST("bld_trg_arch")), kbuild_get_variable_n(ST("bld_type")), argv[0]); if (pVar) o = variable_buffer_output(o, pVar->value, pVar->value_length); (void)pszFuncName; (void)iVer; return o; } /** * Similar to _TARGET_TOOL since r3415. */ static struct variable * kbuild_get_object_suffix(struct variable *pTarget, struct variable *pSource, struct variable *pTool, struct variable *pType, struct variable *pBldTrg, struct variable *pBldTrgArch, struct variable *pBldType, const char *pszVarName) { struct variable *pVar = kbuild_first_prop(pTarget, pSource, pTool, pType, pBldTrg, pBldTrgArch, pBldType, "SUFF_OBJ", sizeof("SUFF_OBJ") - 1, "OBJSUFF", sizeof("OBJSUFF") - 1, 0 /*fExtendedSource*/, pszVarName); if (!pVar) OSS(fatal, NILF, _("no OBJSUFF attribute or SUFF_OBJ default for source `%s' in target `%s'!"), pSource->value, pTarget->value); return pVar; } /** * "kb-obj-suff [ver=0]" * * Since r3415 an extended set of keyword variations is used on the target, tool * and global properties. */ char * func_kbuild_object_suffix(char *o, char **argv, const char *pszFuncName) { const int iVer = kbuild_version_to_int(argv[1], 1 /*strict*/); struct variable *pVar = kbuild_get_object_suffix(kbuild_get_variable_n(ST("target")), kbuild_get_variable_n(ST("source")), kbuild_get_variable_n(ST("tool")), kbuild_get_variable_n(ST("type")), kbuild_get_variable_n(ST("bld_trg")), kbuild_get_variable_n(ST("bld_trg_arch")), kbuild_get_variable_n(ST("bld_type")), argv[0]); if (pVar) o = variable_buffer_output(o, pVar->value, pVar->value_length); (void)pszFuncName; (void)iVer; return o; } /* ## Figure out where to put object files. # @param $1 source file # @param $2 normalized main target # @remark There are two major hacks here: # 1. Source files in the output directory are translated into a gen/ subdir. # 2. Catch anyone specifying $(PATH_SUB_CURRENT)/sourcefile.c. _OBJECT_BASE = $(PATH_TARGET)/$(2)/$(call no-root-slash,$(call no-drive,$(basename \ $(patsubst $(PATH_ROOT)/%,%,$(patsubst $(PATH_SUB_CURRENT)/%,%,$(patsubst $(PATH_TARGET)/$(2)/%,gen/%,$(1))))))) */ static struct variable * kbuild_get_object_base(struct variable *pTarget, struct variable *pSource, const char *pszVarName) { struct variable *pPathTarget = kbuild_get_variable_n(ST("PATH_TARGET")); struct variable *pPathRoot = kbuild_get_variable_n(ST("PATH_ROOT")); struct variable *pPathSubCur = kbuild_get_variable_n(ST("PATH_SUB_CURRENT")); const char *pszSrcPrefix = NULL; size_t cchSrcPrefix = 0; size_t cchSrc = 0; const char *pszSrcEnd; char *pszSrc; char *pszResult; char *psz; char *pszDot; size_t cch; /* * Strip the source filename of any unnecessary leading path and root specs. */ if ( pSource->value_length > pPathTarget->value_length && !strncmp(pSource->value, pPathTarget->value, pPathTarget->value_length)) { pszSrc = pSource->value + pPathTarget->value_length; pszSrcPrefix = "gen/"; cchSrcPrefix = sizeof("gen/") - 1; if ( *pszSrc == '/' && !strncmp(pszSrc + 1, pTarget->value, pTarget->value_length) && ( pszSrc[pTarget->value_length + 1] == '/' || pszSrc[pTarget->value_length + 1] == '\0')) pszSrc += 1 + pTarget->value_length; } else if ( pSource->value_length > pPathRoot->value_length && !strncmp(pSource->value, pPathRoot->value, pPathRoot->value_length)) { pszSrc = pSource->value + pPathRoot->value_length; if ( *pszSrc == '/' && !strncmp(pszSrc + 1, pPathSubCur->value, pPathSubCur->value_length) && ( pszSrc[pPathSubCur->value_length + 1] == '/' || pszSrc[pPathSubCur->value_length + 1] == '\0')) pszSrc += 1 + pPathSubCur->value_length; } else pszSrc = pSource->value; /* skip root specification */ #ifdef HAVE_DOS_PATHS if (isalpha(pszSrc[0]) && pszSrc[1] == ':') pszSrc += 2; #endif while (*pszSrc == '/' #ifdef HAVE_DOS_PATHS || *pszSrc == '\\' #endif ) pszSrc++; /* drop the source extension. */ pszSrcEnd = pSource->value + pSource->value_length; for (;;) { pszSrcEnd--; if ( pszSrcEnd <= pszSrc || *pszSrcEnd == '/' #ifdef HAVE_DOS_PATHS || *pszSrcEnd == '\\' || *pszSrcEnd == ':' #endif ) { pszSrcEnd = pSource->value + pSource->value_length; break; } if (*pszSrcEnd == '.') break; } /* * Assemble the string on the heap and define the objbase variable * which we then return. */ cchSrc = pszSrcEnd - pszSrc; cch = pPathTarget->value_length + 1 /* slash */ + pTarget->value_length + 1 /* slash */ + cchSrcPrefix + cchSrc + 1; psz = pszResult = xmalloc(cch); memcpy(psz, pPathTarget->value, pPathTarget->value_length); psz += pPathTarget->value_length; *psz++ = '/'; memcpy(psz, pTarget->value, pTarget->value_length); psz += pTarget->value_length; *psz++ = '/'; if (pszSrcPrefix) { memcpy(psz, pszSrcPrefix, cchSrcPrefix); psz += cchSrcPrefix; } pszDot = psz; memcpy(psz, pszSrc, cchSrc); psz += cchSrc; *psz = '\0'; /* convert '..' path elements in the source to 'dt'. */ while ((pszDot = memchr(pszDot, '.', psz - pszDot)) != NULL) { if ( pszDot[1] == '.' && ( pszDot == psz || pszDot[-1] == '/' #ifdef HAVE_DOS_PATHS || pszDot[-1] == '\\' || pszDot[-1] == ':' #endif ) && ( !pszDot[2] || pszDot[2] == '/' #ifdef HAVE_DOS_PATHS || pszDot[2] == '\\' || pszDot[2] == ':' #endif ) ) { *pszDot++ = 'd'; *pszDot++ = 't'; } else pszDot++; } /* * Define the variable in the current set and return it. */ return define_variable_vl(pszVarName, strlen(pszVarName), pszResult, cch - 1, 0 /* use pszResult */, o_local, 0 /* !recursive */); } /** * "kb-obj-base [ver]" Implements _OBJECT_BASE. */ char * func_kbuild_object_base(char *o, char **argv, const char *pszFuncName) { const int iVer = kbuild_version_to_int(argv[1], 1 /*strict*/); struct variable *pVar = kbuild_get_object_base(kbuild_lookup_variable("target"), kbuild_lookup_variable("source"), argv[0]); if (pVar) o = variable_buffer_output(o, pVar->value, pVar->value_length); (void)pszFuncName; (void)iVer; return o; } struct kbuild_sdks { char *apsz[4]; struct variable *pa; unsigned c; unsigned iGlobal; unsigned cGlobal; unsigned iTarget; unsigned cTarget; unsigned iSource; unsigned cSource; unsigned iTargetSource; unsigned cTargetSource; unsigned int cchMax; }; /* Fills in the SDK struct (remember to free it). */ static void kbuild_get_sdks(struct kbuild_sdks *pSdks, struct variable *pTarget, struct variable *pSource, struct variable *pBldType, struct variable *pBldTrg, struct variable *pBldTrgArch) { unsigned i; unsigned j; size_t cchTmp, cch; char *pszTmp; unsigned cchCur; char *pszCur; const char *pszIterator; /** @todo rewrite this to avoid sprintf and allocated_varaible_expand_2. */ /* basic init. */ pSdks->cchMax = 0; pSdks->pa = NULL; pSdks->c = 0; i = 0; /* determin required tmp variable name space. */ cchTmp = sizeof("$(__SDKS) $(__SDKS.) $(__SDKS.) $(__SDKS.) $(__SDKS..)") + (pTarget->value_length + pSource->value_length) * 5 + pBldType->value_length + pBldTrg->value_length + pBldTrgArch->value_length + pBldTrg->value_length + pBldTrgArch->value_length; pszTmp = alloca(cchTmp); /* the global sdks. */ pSdks->iGlobal = i; pSdks->cGlobal = 0; cch = sprintf(pszTmp, "$(SDKS) $(SDKS.%s) $(SDKS.%s) $(SDKS.%s) $(SDKS.%s.%s)", pBldType->value, pBldTrg->value, pBldTrgArch->value, pBldTrg->value, pBldTrgArch->value); pszIterator = pSdks->apsz[0] = allocated_variable_expand_2(pszTmp, cch, NULL); while ((pszCur = find_next_token(&pszIterator, &cchCur)) != 0) pSdks->cGlobal++; i += pSdks->cGlobal; /* the target sdks.*/ pSdks->iTarget = i; pSdks->cTarget = 0; cch = sprintf(pszTmp, "$(%s_SDKS) $(%s_SDKS.%s) $(%s_SDKS.%s) $(%s_SDKS.%s) $(%s_SDKS.%s.%s)", pTarget->value, pTarget->value, pBldType->value, pTarget->value, pBldTrg->value, pTarget->value, pBldTrgArch->value, pTarget->value, pBldTrg->value, pBldTrgArch->value); pszIterator = pSdks->apsz[1] = allocated_variable_expand_2(pszTmp, cch, NULL); while ((pszCur = find_next_token(&pszIterator, &cchCur)) != 0) pSdks->cTarget++; i += pSdks->cTarget; /* the source sdks.*/ pSdks->iSource = i; pSdks->cSource = 0; cch = sprintf(pszTmp, "$(%s_SDKS) $(%s_SDKS.%s) $(%s_SDKS.%s) $(%s_SDKS.%s) $(%s_SDKS.%s.%s)", pSource->value, pSource->value, pBldType->value, pSource->value, pBldTrg->value, pSource->value, pBldTrgArch->value, pSource->value, pBldTrg->value, pBldTrgArch->value); pszIterator = pSdks->apsz[2] = allocated_variable_expand_2(pszTmp, cch, NULL); while ((pszCur = find_next_token(&pszIterator, &cchCur)) != 0) pSdks->cSource++; i += pSdks->cSource; /* the target + source sdks. */ pSdks->iTargetSource = i; pSdks->cTargetSource = 0; cch = sprintf(pszTmp, "$(%s_%s_SDKS) $(%s_%s_SDKS.%s) $(%s_%s_SDKS.%s) $(%s_%s_SDKS.%s) $(%s_%s_SDKS.%s.%s)", pTarget->value, pSource->value, pTarget->value, pSource->value, pBldType->value, pTarget->value, pSource->value, pBldTrg->value, pTarget->value, pSource->value, pBldTrgArch->value, pTarget->value, pSource->value, pBldTrg->value, pBldTrgArch->value); assert(cch < cchTmp); (void)cch; pszIterator = pSdks->apsz[3] = allocated_variable_expand_2(pszTmp, cch, NULL); while ((pszCur = find_next_token(&pszIterator, &cchCur)) != 0) pSdks->cTargetSource++; i += pSdks->cTargetSource; pSdks->c = i; if (!i) return; /* * Allocate the variable array and create the variables. */ pSdks->pa = (struct variable *)xmalloc(sizeof(pSdks->pa[0]) * i); memset(pSdks->pa, 0, sizeof(pSdks->pa[0]) * i); for (i = j = 0; j < sizeof(pSdks->apsz) / sizeof(pSdks->apsz[0]); j++) { pszIterator = pSdks->apsz[j]; while ((pszCur = find_next_token(&pszIterator, &cchCur)) != 0) { pSdks->pa[i].value = pszCur; pSdks->pa[i].value_length = cchCur; i++; } } assert(i == pSdks->c); /* terminate them (find_next_token won't work if we terminate them in the previous loop). */ while (i-- > 0) { pSdks->pa[i].value[pSdks->pa[i].value_length] = '\0'; /* calc the max variable length too. */ if (pSdks->cchMax < (unsigned int)pSdks->pa[i].value_length) pSdks->cchMax = pSdks->pa[i].value_length; } } /* releases resources allocated in the kbuild_get_sdks. */ static void kbuild_put_sdks(struct kbuild_sdks *pSdks) { unsigned j; for (j = 0; j < sizeof(pSdks->apsz) / sizeof(pSdks->apsz[0]); j++) free(pSdks->apsz[j]); free(pSdks->pa); } /** Per variable struct used by kbuild_collect_source_prop and helpers. */ struct kbuild_collect_variable { struct variable *pVar; unsigned int cchExp; char *pszExp; }; /** * Helper for kbuild_collect_source_prop. * * Frees up any memory we've allocated for the variable values. */ static struct variable * kbuild_collect_source_prop_create_var(size_t cchTotal, int cVars, struct kbuild_collect_variable *paVars, int iDirection, const char *pszVarName, size_t cchVarName, enum variable_origin enmVarOrigin) { char *pszResult; struct variable *pVar; if (!cVars || !cchTotal) { pszResult = xmalloc(1); *pszResult = '\0'; } else { int iVar; char *psz = pszResult = xmalloc(cchTotal + 1); if (iDirection == 1) { for (iVar = 0; iVar < cVars; iVar++) { my_memcpy(psz, paVars[iVar].pszExp, paVars[iVar].cchExp); psz += paVars[iVar].cchExp; *psz++ = ' '; if (paVars[iVar].pszExp != paVars[iVar].pVar->value) free(paVars[iVar].pszExp); } } else { iVar = cVars; while (iVar-- > 0) { my_memcpy(psz, paVars[iVar].pszExp, paVars[iVar].cchExp); psz += paVars[iVar].cchExp; *psz++ = ' '; if (paVars[iVar].pszExp != paVars[iVar].pVar->value) free(paVars[iVar].pszExp); } } assert(psz != pszResult); assert(cchTotal == (size_t)(psz - pszResult)); psz[-1] = '\0'; cchTotal--; } if (enmVarOrigin == o_local) pVar = define_variable_vl(pszVarName, cchVarName, pszResult, cchTotal, 0 /* take pszResult */ , enmVarOrigin, 0 /* !recursive */); else pVar = define_variable_vl_global(pszVarName, cchVarName, pszResult, cchTotal, 0 /* take pszResult */ , enmVarOrigin, 0 /* !recursive */, NILF); return pVar; } /* this kind of stuff: defs := $(kb-src-exp defs) $(TOOL_$(tool)_DEFS)\ $(TOOL_$(tool)_DEFS.$(bld_type))\ $(TOOL_$(tool)_DEFS.$(bld_trg))\ $(TOOL_$(tool)_DEFS.$(bld_trg_arch))\ $(TOOL_$(tool)_DEFS.$(bld_trg).$(bld_trg_arch))\ $(TOOL_$(tool)_DEFS.$(bld_trg_cpu))\ $(TOOL_$(tool)_$(type)DEFS)\ $(TOOL_$(tool)_$(type)DEFS.$(bld_type))\ $(foreach sdk, $(SDKS.$(bld_trg)) \ $(SDKS.$(bld_trg).$(bld_trg_arch)) \ $(SDKS.$(bld_type)) \ $(SDKS),\ $(SDK_$(sdk)_DEFS)\ $(SDK_$(sdk)_DEFS.$(bld_type))\ $(SDK_$(sdk)_DEFS.$(bld_trg))\ $(SDK_$(sdk)_DEFS.$(bld_trg_arch))\ $(SDK_$(sdk)_DEFS.$(bld_trg).$(bld_trg_arch))\ $(SDK_$(sdk)_DEFS.$(bld_trg_cpu))\ $(SDK_$(sdk)_$(type)DEFS)\ $(SDK_$(sdk)_$(type)DEFS.$(bld_type))\ $(SDK_$(sdk)_$(type)DEFS.$(bld_trg))\ $(SDK_$(sdk)_$(type)DEFS.$(bld_trg_arch))\ $(SDK_$(sdk)_$(type)DEFS.$(bld_trg).$(bld_trg_arch))\ $(SDK_$(sdk)_$(type)DEFS.$(bld_trg_cpu)))\ $(DEFS)\ $(DEFS.$(bld_type))\ $(DEFS.$(bld_trg))\ $(DEFS.$(bld_trg_arch))\ $(DEFS.$(bld_trg).$(bld_trg_arch))\ $(DEFS.$(bld_trg_cpu))\ $($(type)DEFS)\ $($(type)DEFS.$(bld_type))\ $($(type)DEFS.$(bld_trg))\ $($(type)DEFS.$(bld_trg_arch))\ $($(type)DEFS.$(bld_trg).$(bld_trg_arch))\ $($(type)DEFS.$(bld_trg_cpu))\ $(foreach sdk, $($(target)_SDKS.$(bld_trg)) \ $($(target)_SDKS.$(bld_trg).$(bld_trg_arch)) \ $($(target)_SDKS.$(bld_type)) \ $($(target)_SDKS),\ $(SDK_$(sdk)_DEFS)\ $(SDK_$(sdk)_DEFS.$(bld_type))\ $(SDK_$(sdk)_DEFS.$(bld_trg))\ $(SDK_$(sdk)_DEFS.$(bld_trg_arch))\ $(SDK_$(sdk)_DEFS.$(bld_trg).$(bld_trg_arch))\ $(SDK_$(sdk)_DEFS.$(bld_trg_cpu))\ $(SDK_$(sdk)_$(type)DEFS)\ $(SDK_$(sdk)_$(type)DEFS.$(bld_type))\ $(SDK_$(sdk)_$(type)DEFS.$(bld_trg))\ $(SDK_$(sdk)_$(type)DEFS.$(bld_trg_arch))\ $(SDK_$(sdk)_$(type)DEFS.$(bld_trg).$(bld_trg_arch))\ $(SDK_$(sdk)_$(type)DEFS.$(bld_trg_cpu)))\ $($(target)_DEFS)\ $($(target)_DEFS.$(bld_type))\ $($(target)_DEFS.$(bld_trg))\ $($(target)_DEFS.$(bld_trg_arch))\ $($(target)_DEFS.$(bld_trg).$(bld_trg_arch))\ $($(target)_DEFS.$(bld_trg_cpu))\ $($(target)_$(type)DEFS)\ $($(target)_$(type)DEFS.$(bld_type))\ $($(target)_$(type)DEFS.$(bld_trg))\ $($(target)_$(type)DEFS.$(bld_trg_arch))\ $($(target)_$(type)DEFS.$(bld_trg).$(bld_trg_arch))\ $($(target)_$(type)DEFS.$(bld_trg_cpu))\ $(foreach sdk, $($(source)_SDKS.$(bld_trg)) \ $($(source)_SDKS.$(bld_trg).$(bld_trg_arch)) \ $($(source)_SDKS.$(bld_type)) \ $($(source)_SDKS),\ $(SDK_$(sdk)_DEFS)\ $(SDK_$(sdk)_DEFS.$(bld_type))\ $(SDK_$(sdk)_DEFS.$(bld_trg))\ $(SDK_$(sdk)_DEFS.$(bld_trg_arch))\ $(SDK_$(sdk)_DEFS.$(bld_trg).$(bld_trg_arch))\ $(SDK_$(sdk)_DEFS.$(bld_trg_cpu))\ $(SDK_$(sdk)_$(type)DEFS)\ $(SDK_$(sdk)_$(type)DEFS.$(bld_type))\ $(SDK_$(sdk)_$(type)DEFS.$(bld_trg))\ $(SDK_$(sdk)_$(type)DEFS.$(bld_trg_arch))\ $(SDK_$(sdk)_$(type)DEFS.$(bld_trg).$(bld_trg_arch))\ $(SDK_$(sdk)_$(type)DEFS.$(bld_trg_cpu)))\ $($(source)_DEFS)\ $($(source)_DEFS.$(bld_type))\ $($(source)_DEFS.$(bld_trg))\ $($(source)_DEFS.$(bld_trg_arch))\ $($(source)_DEFS.$(bld_trg).$(bld_trg_arch))\ $($(source)_DEFS.$(bld_trg_cpu))\ $($(source)_$(type)DEFS)\ $($(source)_$(type)DEFS.$(bld_type))\ $($(source)_$(type)DEFS.$(bld_trg))\ $($(source)_$(type)DEFS.$(bld_trg_arch))\ $($(source)_$(type)DEFS.$(bld_trg).$(bld_trg_arch))\ $($(source)_$(type)DEFS.$(bld_trg_cpu))\ $(foreach sdk, $($(target)_$(source)_SDKS.$(bld_trg)) \ $($(target)_$(source)_SDKS.$(bld_trg).$(bld_trg_arch)) \ $($(target)_$(source)_SDKS.$(bld_type)) \ $($(target)_$(source)_SDKS),\ $(SDK_$(sdk)_DEFS)\ $(SDK_$(sdk)_DEFS.$(bld_type))\ $(SDK_$(sdk)_DEFS.$(bld_trg))\ $(SDK_$(sdk)_DEFS.$(bld_trg_arch))\ $(SDK_$(sdk)_DEFS.$(bld_trg).$(bld_trg_arch))\ $(SDK_$(sdk)_DEFS.$(bld_trg_cpu))\ $(SDK_$(sdk)_$(type)DEFS)\ $(SDK_$(sdk)_$(type)DEFS.$(bld_type))\ $(SDK_$(sdk)_$(type)DEFS.$(bld_trg))\ $(SDK_$(sdk)_$(type)DEFS.$(bld_trg_arch))\ $(SDK_$(sdk)_$(type)DEFS.$(bld_trg).$(bld_trg_arch))\ $(SDK_$(sdk)_$(type)DEFS.$(bld_trg_cpu)))\ $($(target)_$(source)_DEFS)\ $($(target)_$(source)_DEFS.$(bld_type))\ $($(target)_$(source)_DEFS.$(bld_trg))\ $($(target)_$(source)_DEFS.$(bld_trg_arch))\ $($(target)_$(source)_DEFS.$(bld_trg).$(bld_trg_arch))\ $($(target)_$(source)_DEFS.$(bld_trg_cpu))\ $($(target)_$(source)_$(type)DEFS)\ $($(target)_$(source)_$(type)DEFS.$(bld_type))\ $($(target)_$(source)_$(type)DEFS.$(bld_trg))\ $($(target)_$(source)_$(type)DEFS.$(bld_trg_arch))\ $($(target)_$(source)_$(type)DEFS.$(bld_trg).$(bld_trg_arch))\ $($(target)_$(source)_$(type)DEFS.$(bld_trg_cpu)) */ static struct variable * kbuild_collect_source_prop(struct variable *pTarget, struct variable *pSource, struct variable *pTool, struct kbuild_sdks *pSdks, struct variable *pType, struct variable *pBldType, struct variable *pBldTrg, struct variable *pBldTrgArch, struct variable *pBldTrgCpu, struct variable *pDefPath, const char *pszProp, size_t cchProp, const char *pszVarName, size_t cchVarName, int iDirection) { struct variable *pVar; unsigned iSdk, iSdkEnd; int cVars, iVar; size_t cchTotal, cchBuf; char *pszBuf, *psz, *psz2, *psz3; struct kbuild_collect_variable *paVars; assert(iDirection == 1 || iDirection == -1); /* * Calc and allocate a too big name buffer. */ cchBuf = cchProp + 1 + pTarget->value_length + 1 + pSource->value_length + 1 + pSdks->cchMax + 1 + (pTool ? pTool->value_length + 1 : 0) + pType->value_length + 1 + pBldTrg->value_length + 1 + pBldTrgArch->value_length + 1 + pBldTrgCpu->value_length + 1 + pBldType->value_length + 1 + sizeof("_2_"); pszBuf = xmalloc(cchBuf); /* * Get the variables. * * The compiler will get a heart attack when it sees this code ... ;-) */ cVars = 12 * (pSdks->c + 5); paVars = (struct kbuild_collect_variable *)alloca(cVars * sizeof(paVars[0])); iVar = 0; cchTotal = 0; #define ADD_VAR(pVar) do { my_memcpy(psz, (pVar)->value, (pVar)->value_length); psz += (pVar)->value_length; } while (0) #define ADD_STR(pszStr, cchStr) do { my_memcpy(psz, (pszStr), (cchStr)); psz += (cchStr); } while (0) #define ADD_CSTR(pszStr) do { my_memcpy(psz, pszStr, sizeof(pszStr) - 1); psz += sizeof(pszStr) - 1; } while (0) #define ADD_CH(ch) do { *psz++ = (ch); } while (0) #define DO_VAR_LOOKUP() \ do { \ pVar = kbuild_lookup_variable_n(pszBuf, psz - pszBuf); \ if (pVar) \ { \ paVars[iVar].pVar = pVar; \ if ( !pVar->recursive \ || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR(pVar)) \ { \ paVars[iVar].pszExp = pVar->value; \ paVars[iVar].cchExp = pVar->value_length; \ if (pDefPath && paVars[iVar].cchExp) \ kbuild_apply_defpath(pDefPath, &paVars[iVar].pszExp, &paVars[iVar].cchExp, NULL, 0); \ if (paVars[iVar].cchExp) \ { \ cchTotal += paVars[iVar].cchExp + 1; \ iVar++; \ } \ } \ else \ { \ paVars[iVar].pszExp = allocated_variable_expand_2(pVar->value, pVar->value_length, &paVars[iVar].cchExp); \ if (pDefPath && paVars[iVar].cchExp) \ kbuild_apply_defpath(pDefPath, &paVars[iVar].pszExp, &paVars[iVar].cchExp, NULL, 1); \ if (paVars[iVar].cchExp) \ { \ cchTotal += paVars[iVar].cchExp + 1; \ iVar++; \ } \ else \ free(paVars[iVar].pszExp); \ } \ } \ } while (0) #define DO_SINGLE_PSZ3_VARIATION() \ do { \ DO_VAR_LOOKUP(); \ \ ADD_CH('.'); \ psz3 = psz; \ ADD_VAR(pBldType); \ DO_VAR_LOOKUP(); \ \ psz = psz3; \ ADD_VAR(pBldTrg); \ DO_VAR_LOOKUP(); \ \ psz = psz3; \ ADD_VAR(pBldTrgArch); \ DO_VAR_LOOKUP(); \ \ psz = psz3; \ ADD_VAR(pBldTrg); \ ADD_CH('.'); \ ADD_VAR(pBldTrgArch); \ DO_VAR_LOOKUP(); \ \ psz = psz3; \ ADD_VAR(pBldTrgCpu); \ DO_VAR_LOOKUP(); \ } while (0) #define DO_DOUBLE_PSZ2_VARIATION() \ do { \ psz2 = psz; \ ADD_STR(pszProp, cchProp); \ DO_SINGLE_PSZ3_VARIATION(); \ \ /* add prop before type */ \ psz = psz2; \ ADD_VAR(pType); \ ADD_STR(pszProp, cchProp); \ DO_SINGLE_PSZ3_VARIATION(); \ } while (0) /* * The first bunch part may be cached on the target already, check that first: */ psz = pszBuf; ADD_VAR(pTarget); ADD_CSTR("_2_"); ADD_VAR(pType); ADD_STR(pszProp, cchProp); ADD_CH('_'); ADD_VAR(pBldType); *psz = '\0'; pVar = kbuild_lookup_variable_n(pszBuf, psz - pszBuf); if (pVar) assert(iVar == 0); else { /* the tool (lowest priority). */ psz = pszBuf; ADD_CSTR("TOOL_"); ADD_VAR(pTool); ADD_CH('_'); DO_DOUBLE_PSZ2_VARIATION(); /* the global sdks. */ iSdkEnd = iDirection == 1 ? pSdks->iGlobal + pSdks->cGlobal : pSdks->iGlobal - 1; for (iSdk = iDirection == 1 ? pSdks->iGlobal : pSdks->iGlobal + pSdks->cGlobal - 1; iSdk != iSdkEnd; iSdk += iDirection) { struct variable *pSdk = &pSdks->pa[iSdk]; psz = pszBuf; ADD_CSTR("SDK_"); ADD_VAR(pSdk); ADD_CH('_'); DO_DOUBLE_PSZ2_VARIATION(); } /* the globals. */ psz = pszBuf; DO_DOUBLE_PSZ2_VARIATION(); /* the target sdks. */ iSdkEnd = iDirection == 1 ? pSdks->iTarget + pSdks->cTarget : pSdks->iTarget - 1; for (iSdk = iDirection == 1 ? pSdks->iTarget : pSdks->iTarget + pSdks->cTarget - 1; iSdk != iSdkEnd; iSdk += iDirection) { struct variable *pSdk = &pSdks->pa[iSdk]; psz = pszBuf; ADD_CSTR("SDK_"); ADD_VAR(pSdk); ADD_CH('_'); DO_DOUBLE_PSZ2_VARIATION(); } /* the target. */ psz = pszBuf; ADD_VAR(pTarget); ADD_CH('_'); DO_DOUBLE_PSZ2_VARIATION(); /* * Cache the result thus far (frees up anything we might've allocated above). */ psz = pszBuf; ADD_VAR(pTarget); ADD_CSTR("_2_"); ADD_VAR(pType); ADD_STR(pszProp, cchProp); ADD_CH('_'); ADD_VAR(pBldType); *psz = '\0'; assert(iVar <= cVars); pVar = kbuild_collect_source_prop_create_var(cchTotal, iVar, paVars, iDirection, pszBuf, psz - pszBuf, o_file); assert(pVar); } /* Now we use the cached target variable. */ paVars[0].pVar = pVar; paVars[0].pszExp = pVar->value; paVars[0].cchExp = pVar->value_length; cchTotal = paVars[0].cchExp + 1; iVar = 1; /* the source sdks. */ iSdkEnd = iDirection == 1 ? pSdks->iSource + pSdks->cSource : pSdks->iSource - 1; for (iSdk = iDirection == 1 ? pSdks->iSource : pSdks->iSource + pSdks->cSource - 1; iSdk != iSdkEnd; iSdk += iDirection) { struct variable *pSdk = &pSdks->pa[iSdk]; psz = pszBuf; ADD_CSTR("SDK_"); ADD_VAR(pSdk); ADD_CH('_'); DO_DOUBLE_PSZ2_VARIATION(); } /* the source. */ psz = pszBuf; ADD_VAR(pSource); ADD_CH('_'); DO_DOUBLE_PSZ2_VARIATION(); /* the target + source sdks. */ iSdkEnd = iDirection == 1 ? pSdks->iTargetSource + pSdks->cTargetSource : pSdks->iTargetSource - 1; for (iSdk = iDirection == 1 ? pSdks->iTargetSource : pSdks->iTargetSource + pSdks->cTargetSource - 1; iSdk != iSdkEnd; iSdk += iDirection) { struct variable *pSdk = &pSdks->pa[iSdk]; psz = pszBuf; ADD_CSTR("SDK_"); ADD_VAR(pSdk); ADD_CH('_'); DO_DOUBLE_PSZ2_VARIATION(); } /* the target + source. */ psz = pszBuf; ADD_VAR(pTarget); ADD_CH('_'); ADD_VAR(pSource); ADD_CH('_'); DO_DOUBLE_PSZ2_VARIATION(); free(pszBuf); /* * Construct the result value (frees up anything we might've allocated above). */ assert(iVar <= cVars); return kbuild_collect_source_prop_create_var(cchTotal, iVar, paVars, iDirection, pszVarName, cchVarName, o_local); #undef ADD_VAR #undef ADD_STR #undef ADD_CSTR #undef ADD_CH #undef DO_VAR_LOOKUP #undef DO_DOUBLE_PSZ2_VARIATION #undef DO_SINGLE_PSZ3_VARIATION } /* "kb-src-prop [defpath] [ver]" get a source property. */ char * func_kbuild_source_prop(char *o, char **argv, const char *pszFuncName) { struct variable *pTarget = kbuild_get_variable_n(ST("target")); struct variable *pSource = kbuild_get_variable_n(ST("source")); struct variable *pDefPath = NULL; struct variable *pType = kbuild_get_variable_n(ST("type")); struct variable *pTool = kbuild_get_variable_n(ST("tool")); struct variable *pBldType = kbuild_get_variable_n(ST("bld_type")); struct variable *pBldTrg = kbuild_get_variable_n(ST("bld_trg")); struct variable *pBldTrgArch = kbuild_get_variable_n(ST("bld_trg_arch")); struct variable *pBldTrgCpu = kbuild_get_variable_n(ST("bld_trg_cpu")); struct variable *pVar; struct kbuild_sdks Sdks; int iVer = 0; int iDirection; if (!strcmp(argv[2], "left-to-right")) iDirection = 1; else if (!strcmp(argv[2], "right-to-left")) iDirection = -1; else OS(fatal, NILF, _("incorrect direction argument `%s'!"), argv[2]); if (argv[3]) { const char *psz = argv[3]; while (ISSPACE(*psz)) psz++; if (*psz) pDefPath = kbuild_get_variable_n(ST("defpath")); if (argv[4]) iVer = kbuild_version_to_int(argv[4], 1 /*strict*/); } kbuild_get_sdks(&Sdks, pTarget, pSource, pBldType, pBldTrg, pBldTrgArch); pVar = kbuild_collect_source_prop(pTarget, pSource, pTool, &Sdks, pType, pBldType, pBldTrg, pBldTrgArch, pBldTrgCpu, pDefPath, argv[0], strlen(argv[0]), argv[1], strlen(argv[1]), iDirection); if (pVar) o = variable_buffer_output(o, pVar->value, pVar->value_length); kbuild_put_sdks(&Sdks); (void)pszFuncName; (void)iVer; return o; } /* dep := $(obj)$(SUFF_DEP) obj := $(outbase)$(objsuff) dirdep := $(call DIRDEP,$(dir $(outbase))) PATH_$(target)_$(source) := $(patsubst %/,%,$(dir $(outbase))) */ static struct variable * kbuild_set_object_name_and_dep_and_dirdep_and_PATH_target_source(struct variable *pTarget, struct variable *pSource, struct variable *pOutBase, struct variable *pObjSuff, const char *pszVarName, struct variable **ppDep, struct variable **ppDirDep) { struct variable *pDepSuff = kbuild_get_variable_n(ST("SUFF_DEP")); struct variable *pObj; size_t cch = pOutBase->value_length + pObjSuff->value_length + pDepSuff->value_length + 1; char *pszResult = alloca(cch); char *pszName, *psz; /* * dep. */ psz = pszResult; memcpy(psz, pOutBase->value, pOutBase->value_length); psz += pOutBase->value_length; memcpy(psz, pObjSuff->value, pObjSuff->value_length); psz += pObjSuff->value_length; memcpy(psz, pDepSuff->value, pDepSuff->value_length + 1); *ppDep = define_variable_vl("dep", 3, pszResult, cch - 1, 1 /*dup*/, o_local, 0 /* !recursive */); /* * obj */ *psz = '\0'; pObj = define_variable_vl(pszVarName, strlen(pszVarName), pszResult, psz - pszResult, 1/* dup */, o_local, 0 /* !recursive */); /* * PATH_$(target)_$(source) - this is global! */ /* calc variable name. */ cch = sizeof("PATH_")-1 + pTarget->value_length + sizeof("_")-1 + pSource->value_length; psz = pszName = alloca(cch + 1); memcpy(psz, "PATH_", sizeof("PATH_") - 1); psz += sizeof("PATH_") - 1; memcpy(psz, pTarget->value, pTarget->value_length); psz += pTarget->value_length; *psz++ = '_'; memcpy(psz, pSource->value, pSource->value_length + 1); /* strip off the filename. */ psz = pszResult + pOutBase->value_length; for (;;) { psz--; if (psz <= pszResult) OS(fatal, NULL, "whut!?! no path? result=`%s'", pszResult); #ifdef HAVE_DOS_PATHS if (*psz == ':') { psz++; break; } #endif if ( *psz == '/' #ifdef HAVE_DOS_PATHS || *psz == '\\' #endif ) { while ( psz - 1 > pszResult && psz[-1] == '/' #ifdef HAVE_DOS_PATHS || psz[-1] == '\\' #endif ) psz--; #ifdef HAVE_DOS_PATHS if (psz == pszResult + 2 && pszResult[1] == ':') psz++; #endif break; } } *psz = '\0'; /* set global variable */ define_variable_vl_global(pszName, cch, pszResult, psz - pszResult, 1/*dup*/, o_file, 0 /* !recursive */, NILF); /* * dirdep */ if ( psz[-1] != '/' #ifdef HAVE_DOS_PATHS && psz[-1] != '\\' && psz[-1] != ':' #endif ) { *psz++ = '/'; *psz = '\0'; } *ppDirDep = define_variable_vl("dirdep", 6, pszResult, psz - pszResult, 1/*dup*/, o_local, 0 /* !recursive */); return pObj; } /** * * * Setup the base variables for def_target_source_c_cpp_asm_new: * @code X := $(kb-src-tool tool) x := $(kb-obj-base outbase) x := $(kb-obj-suff objsuff) obj := $(outbase)$(objsuff) PATH_$(target)_$(source) := $(patsubst %/,%,$(dir $(outbase))) x := $(kb-src-prop DEFS,defs,left-to-right) x := $(kb-src-prop INCS,incs,right-to-left) x := $(kb-src-prop FLAGS,flags,right-to-left) x := $(kb-src-prop DEPS,deps,left-to-right) dirdep := $(call DIRDEP,$(dir $(outbase))) dep := $(obj)$(SUFF_DEP) * @endcode * * argv[0] is the function version. Prior to r1792 (early 0.1.5) this * was undefined and footer.kmk always passed an empty string. * * Version 2, as implemented in r1797, will make use of the async * includedep queue feature. This means the files will be read by one or * more background threads, leaving the eval'ing to be done later on by * the main thread (in snap_deps). * * Version 3, as implemented in rXXXX, will check * TOOL_$(tool)_COMPILE_$(type)_USES_KOBJCACHE and use * def_target_source_rule_v3plus_objcache if it's non-empty or * def_target_source_rule_v3plus if it's empty (version 2 and older will use * def_target_source_rule). Version 3 will also skip defining several * properties that it considers legacy (todo: which). * * Version 4, as implemented in r3415, will use the extended tool resolution at * the target level (not source) as implemented by the _TARGET_TOOL update in * r2433. * * With r3415 the $(target)_2_$(type)TOOL will be used for caching purposes * regardless of version. */ char * func_kbuild_source_one(char *o, char **argv, const char *pszFuncName) { static int s_fNoCompileDepsDefined = -1; struct variable *pTarget = kbuild_get_variable_n(ST("target")); struct variable *pSource = kbuild_get_variable_n(ST("source")); struct variable *pDefPath = kbuild_get_variable_n(ST("defpath")); struct variable *pType = kbuild_get_variable_n(ST("type")); struct variable *pBldType = kbuild_get_variable_n(ST("bld_type")); struct variable *pBldTrg = kbuild_get_variable_n(ST("bld_trg")); struct variable *pBldTrgArch= kbuild_get_variable_n(ST("bld_trg_arch")); struct variable *pBldTrgCpu = kbuild_get_variable_n(ST("bld_trg_cpu")); const int iVer = kbuild_version_to_int(argv[0], 0 /*strict*/); struct variable *pTool = kbuild_get_source_tool(pTarget, pSource, pType, pBldTrg, pBldTrgArch, iVer >= 4 ? pBldType : NULL, "tool"); struct variable *pOutBase = kbuild_get_object_base(pTarget, pSource, "outbase"); struct variable *pObjSuff = kbuild_get_object_suffix(pTarget, pSource, pTool, pType, pBldTrg, pBldTrgArch, iVer >= 4 ? pBldType : NULL, "objsuff"); struct variable *pDeps, *pOrderDeps, *pDirDep, *pDep, *pVar, *pOutput, *pOutputMaybe; #if 0 /* not used */ struct variable *pDefs, *pIncs, *pFlags; #endif struct variable *pObj = kbuild_set_object_name_and_dep_and_dirdep_and_PATH_target_source(pTarget, pSource, pOutBase, pObjSuff, "obj", &pDep, &pDirDep); int fInstallOldObjsVar = 0; char *pszDstVar, *pszDst, *pszSrcVar, *pszSrc, *pszVal, *psz; char *pszSavedVarBuf; unsigned cchSavedVarBuf; size_t cch; struct kbuild_sdks Sdks; /* * Gather properties. */ kbuild_get_sdks(&Sdks, pTarget, pSource, pBldType, pBldTrg, pBldTrgArch); if (pDefPath && !pDefPath->value_length) pDefPath = NULL; /*pDefs =*/ kbuild_collect_source_prop(pTarget, pSource, pTool, &Sdks, pType, pBldType, pBldTrg, pBldTrgArch, pBldTrgCpu, NULL, ST("DEFS"), ST("defs"), 1 /* left-to-right */); /*pIncs =*/ kbuild_collect_source_prop(pTarget, pSource, pTool, &Sdks, pType, pBldType, pBldTrg, pBldTrgArch, pBldTrgCpu, pDefPath, ST("INCS"), ST("incs"), -1 /* right-to-left */); /*pFlags =*/ kbuild_collect_source_prop(pTarget, pSource, pTool, &Sdks, pType, pBldType, pBldTrg, pBldTrgArch, pBldTrgCpu, NULL, ST("FLAGS"), ST("flags"), 1 /* left-to-right */); pDeps = kbuild_collect_source_prop(pTarget, pSource, pTool, &Sdks, pType, pBldType, pBldTrg, pBldTrgArch, pBldTrgCpu, pDefPath, ST("DEPS"), ST("deps"), 1 /* left-to-right */); pOrderDeps = kbuild_collect_source_prop(pTarget, pSource, pTool, &Sdks, pType, pBldType, pBldTrg, pBldTrgArch, pBldTrgCpu, pDefPath, ST("ORDERDEPS"), ST("orderdeps"), 1 /* left-to-right */); /* * If we've got a default path, we must expand the source now. * If we do this too early, "_property = stuff" won't work becuase * our 'source' value isn't what the user expects. */ if (pDefPath) { /** @todo assert(pSource->origin != o_automatic); We're changing 'source' * from the foreach loop! */ #ifdef CONFIG_WITH_RDONLY_VARIABLE_VALUE assert(!pSource->rdonly_val); #endif kbuild_apply_defpath(pDefPath, &pSource->value, &pSource->value_length, &pSource->value_alloc_len, 1 /* can free */); } /* # dependencies ifndef NO_COMPILE_DEPS _DEPFILES_INCLUDED += $(dep) $(if $(wildcard $(dep)),$(eval include $(dep))) endif */ if (s_fNoCompileDepsDefined == -1) s_fNoCompileDepsDefined = kbuild_lookup_variable_n(ST("NO_COMPILE_DEPS")) != NULL; if (!s_fNoCompileDepsDefined) { pVar = kbuild_query_recursive_variable_n("_DEPFILES_INCLUDED", sizeof("_DEPFILES_INCLUDED") - 1); if (pVar) { if (pVar->recursive) pVar = kbuild_simplify_variable(pVar); append_string_to_variable(pVar, pDep->value, pDep->value_length, 1 /* append */); } else define_variable_vl_global("_DEPFILES_INCLUDED", sizeof("_DEPFILES_INCLUDED") - 1, pDep->value, pDep->value_length, 1 /* duplicate_value */, o_file, 0 /* recursive */, NULL /* flocp */); eval_include_dep(pDep->value, NILF, iVer >= 2 ? incdep_queue : incdep_read_it); } /* # call the tool $(target)_$(source)_CMDS_ := $(TOOL_$(tool)_COMPILE_$(type)_CMDS) $(target)_$(source)_OUTPUT_ := $(TOOL_$(tool)_COMPILE_$(type)_OUTPUT) $(target)_$(source)_OUTPUT_MAYBE_ := $(TOOL_$(tool)_COMPILE_$(type)_OUTPUT_MAYBE) $(target)_$(source)_DEPEND_ := $(TOOL_$(tool)_COMPILE_$(type)_DEPEND) $(deps) $(source) $(target)_$(source)_DEPORD_ := $(TOOL_$(tool)_COMPILE_$(type)_DEPORD) $(dirdep) */ /** @todo Make all these local variables, if someone needs the info later it * can be recalculated. (Ticket #80.) */ cch = sizeof("TOOL_") + pTool->value_length + sizeof("_COMPILE_") + pType->value_length + sizeof("_USES_KOBJCACHE"); if (cch < pTarget->value_length + sizeof("$(_2_OBJS)")) cch = pTarget->value_length + sizeof("$(_2_OBJS)"); psz = pszSrcVar = alloca(cch); memcpy(psz, "TOOL_", sizeof("TOOL_") - 1); psz += sizeof("TOOL_") - 1; memcpy(psz, pTool->value, pTool->value_length); psz += pTool->value_length; memcpy(psz, "_COMPILE_", sizeof("_COMPILE_") - 1); psz += sizeof("_COMPILE_") - 1; memcpy(psz, pType->value, pType->value_length); psz += pType->value_length; pszSrc = psz; cch = pTarget->value_length + 1 + pSource->value_length + sizeof("_OUTPUT_MAYBE_"); psz = pszDstVar = alloca(cch); memcpy(psz, pTarget->value, pTarget->value_length); psz += pTarget->value_length; *psz++ = '_'; memcpy(psz, pSource->value, pSource->value_length); psz += pSource->value_length; pszDst = psz; memcpy(pszSrc, "_CMDS", sizeof("_CMDS")); memcpy(pszDst, "_CMDS_", sizeof("_CMDS_")); pVar = kbuild_get_recursive_variable(pszSrcVar); if (iVer <= 2) do_variable_definition_2(NILF, pszDstVar, pVar->value, pVar->value_length, !pVar->recursive, 0, o_local, f_simple, 0 /* !target_var */); do_variable_definition_2(NILF, "kbsrc_cmds", pVar->value, pVar->value_length, !pVar->recursive, 0, o_local, f_simple, 0 /* !target_var */); memcpy(pszSrc, "_OUTPUT", sizeof("_OUTPUT")); memcpy(pszDst, "_OUTPUT_", sizeof("_OUTPUT_")); pVar = kbuild_get_recursive_variable(pszSrcVar); if (iVer <= 2) pOutput = do_variable_definition_2(NILF, pszDstVar, pVar->value, pVar->value_length, !pVar->recursive, 0, o_local, f_simple, 0 /* !target_var */); pOutput = do_variable_definition_2(NILF, "kbsrc_output", pVar->value, pVar->value_length, !pVar->recursive, 0, o_local, f_simple, 0 /* !target_var */); memcpy(pszSrc, "_OUTPUT_MAYBE", sizeof("_OUTPUT_MAYBE")); memcpy(pszDst, "_OUTPUT_MAYBE_", sizeof("_OUTPUT_MAYBE_")); pVar = kbuild_query_recursive_variable(pszSrcVar); if (pVar) { if (iVer <= 2) pOutputMaybe = do_variable_definition_2(NILF, pszDstVar, pVar->value, pVar->value_length, !pVar->recursive, 0, o_local, f_simple, 0 /* !target_var */); pOutputMaybe = do_variable_definition_2(NILF, "kbsrc_output_maybe", pVar->value, pVar->value_length, !pVar->recursive, 0, o_local, f_simple, 0 /* !target_var */); } else { if (iVer <= 2) pOutputMaybe = do_variable_definition_2(NILF, pszDstVar, "", 0, 1, 0, o_local, f_simple, 0 /* !target_var */); pOutputMaybe = do_variable_definition_2(NILF, "kbsrc_output_maybe", "", 0, 1, 0, o_local, f_simple, 0 /* !target_var */); } memcpy(pszSrc, "_DEPEND", sizeof("_DEPEND")); memcpy(pszDst, "_DEPEND_", sizeof("_DEPEND_")); pVar = kbuild_get_recursive_variable(pszSrcVar); psz = pszVal = xmalloc(pVar->value_length + 1 + pDeps->value_length + 1 + pSource->value_length + 1); memcpy(psz, pVar->value, pVar->value_length); psz += pVar->value_length; *psz++ = ' '; memcpy(psz, pDeps->value, pDeps->value_length); psz += pDeps->value_length; *psz++ = ' '; memcpy(psz, pSource->value, pSource->value_length + 1); if (iVer <= 2) do_variable_definition_2(NILF, pszDstVar, pszVal, pVar->value_length + 1 + pDeps->value_length + 1 + pSource->value_length, !pVar->recursive && !pDeps->recursive && !pSource->recursive, NULL, o_local, f_simple, 0 /* !target_var */); do_variable_definition_2(NILF, "kbsrc_depend", pszVal, pVar->value_length + 1 + pDeps->value_length + 1 + pSource->value_length, !pVar->recursive && !pDeps->recursive && !pSource->recursive, pszVal, o_local, f_simple, 0 /* !target_var */); memcpy(pszSrc, "_DEPORD", sizeof("_DEPORD")); memcpy(pszDst, "_DEPORD_", sizeof("_DEPORD_")); pVar = kbuild_get_recursive_variable(pszSrcVar); psz = pszVal = xmalloc(pVar->value_length + 1 + pDirDep->value_length + 1 + pOrderDeps->value_length + 1); memcpy(psz, pVar->value, pVar->value_length); psz += pVar->value_length; *psz++ = ' '; memcpy(psz, pDirDep->value, pDirDep->value_length); psz += pDirDep->value_length; *psz++ = ' '; memcpy(psz, pOrderDeps->value, pOrderDeps->value_length + 1); if (iVer <= 2) do_variable_definition_2(NILF, pszDstVar, pszVal, pVar->value_length + 1 + pDirDep->value_length + 1 + pOrderDeps->value_length, !pVar->recursive && !pDirDep->recursive && !pOrderDeps->recursive, NULL, o_local, f_simple, 0 /* !target_var */); do_variable_definition_2(NILF, "kbsrc_depord", pszVal, pVar->value_length + 1 + pDirDep->value_length + 1 + pOrderDeps->value_length, !pVar->recursive && !pDirDep->recursive && !pOrderDeps->recursive, pszVal, o_local, f_simple, 0 /* !target_var */); /* _OUT_FILES += $($(target)_$(source)_OUTPUT_) $($(target)_$(source)_OUTPUT_MAYBE_) */ pVar = kbuild_get_variable_n(ST("_OUT_FILES")); append_string_to_variable(pVar, pOutput->value, pOutput->value_length, 1 /* append */); if (pOutputMaybe->value_length) append_string_to_variable(pVar, pOutputMaybe->value, pOutputMaybe->value_length, 1 /* append */); /* $(target)_2_OBJS += $(obj) */ memcpy(pszDstVar + pTarget->value_length, "_2_OBJS", sizeof("_2_OBJS")); pVar = kbuild_query_recursive_variable_n(pszDstVar, pTarget->value_length + sizeof("_2_OBJS") - 1); fInstallOldObjsVar |= iVer <= 2 && (!pVar || !pVar->value_length); if (pVar) { if (pVar->recursive) pVar = kbuild_simplify_variable(pVar); append_string_to_variable(pVar, pObj->value, pObj->value_length, 1 /* append */); } else define_variable_vl_global(pszDstVar, pTarget->value_length + sizeof("_2_OBJS") - 1, pObj->value, pObj->value_length, 1 /* duplicate_value */, o_file, 0 /* recursive */, NULL /* flocp */); /* * Install legacy variable. */ if (fInstallOldObjsVar) { /* $(target)_OBJS_ = $($(target)_2_OBJS)*/ memcpy(pszDstVar + pTarget->value_length, "_OBJS_", sizeof("_OBJS_")); pszSrcVar[0] = '$'; pszSrcVar[1] = '('; memcpy(pszSrcVar + 2, pTarget->value, pTarget->value_length); psz = pszSrcVar + 2 + pTarget->value_length; memcpy(psz, "_2_OBJS)", sizeof("_2_OBJS)")); define_variable_vl_global(pszDstVar, pTarget->value_length + sizeof("_OBJS_") - 1, pszSrcVar, pTarget->value_length + sizeof("$(_2_OBJS)") - 1, 1 /* duplicate_value */, o_file, 1 /* recursive */, NULL /* flocp */); } /* $(eval $(def_target_source_rule)) */ if (iVer > 2) { /*ifneq ($(TOOL_$(tool)_COMPILE_$(type)_USES_KOBJCACHE),)*/ int fUsesObjCache = 0; memcpy(pszSrc, "_USES_KOBJCACHE", sizeof("_USES_KOBJCACHE")); pVar = lookup_variable(pszSrcVar, pszSrc + sizeof("_USES_KOBJCACHE") - 1 - pszSrcVar); if (pVar) { if ( !pVar->recursive || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR(pVar)) fUsesObjCache = pVar->value_length > 0; else { unsigned int cchTmp = 0; char *pszTmp = allocated_variable_expand_2(pVar->value, pVar->value_length, &cchTmp); free(pszTmp); fUsesObjCache = cchTmp > 0; } } if (!fUsesObjCache) pVar = kbuild_get_recursive_variable("def_target_source_rule_v3plus"); else pVar = kbuild_get_recursive_variable("def_target_source_rule_v3plus_objcache"); } else pVar = kbuild_get_recursive_variable("def_target_source_rule"); pszVal = variable_expand_string_2(o, pVar->value, pVar->value_length, &psz); assert(!((size_t)pszVal & 3)); install_variable_buffer(&pszSavedVarBuf, &cchSavedVarBuf); eval_buffer(pszVal, NULL, psz); restore_variable_buffer(pszSavedVarBuf, cchSavedVarBuf); kbuild_put_sdks(&Sdks); (void)pszFuncName; *pszVal = '\0'; return pszVal; } /* ## Inherit one template property in a non-accumulative manner. # @param $(prop) Property name # @param $(target) Target name # @todo fix the precedence order for some properties. define def_inherit_template_one ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop) ifndef $(target)_$(prop) $(target)_$(prop) := $(TEMPLATE_$($(target)_TEMPLATE)_$(prop)) endif endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg) ifndef $(target)_$(prop).$(bld_trg) $(target)_$(prop).$(bld_trg) := $(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg)) endif endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg).$(bld_trg_arch) ifndef $(target)_$(prop).$(bld_trg).$(bld_trg_arch) $(target)_$(prop).$(bld_trg).$(bld_trg_arch) := $(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg).$(bld_trg_arch)) endif endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg_arch) ifndef $(target)_$(prop).$(bld_trg_arch) $(target)_$(prop).$(bld_trg_arch) := $(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg_arch)) endif endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg_cpu) ifndef $(target)_$(prop).$(bld_trg_cpu) $(target)_$(prop).$(bld_trg_cpu) := $(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg_cpu)) endif endif endef ## Inherit one template property in a non-accumulative manner, deferred expansion. # @param 1: $(prop) Property name # @param 2: $(target) Target name # @todo fix the precedence order for some properties. # @remark this define relies on double evaluation define def_inherit_template_one_deferred ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop) ifndef $(target)_$(prop) $(target)_$(prop) = $$(TEMPLATE_$($(target)_TEMPLATE)_$(prop)) endif endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg) ifndef $(target)_$(prop).$(bld_trg) $(target)_$(prop).$(bld_trg) = $$(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg)) endif endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg).$(bld_trg_arch) ifndef $(target)_$(prop).$(bld_trg).$(bld_trg_arch) $(target)_$(prop).$(bld_trg).$(bld_trg_arch) = $$(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg).$(bld_trg_arch)) endif endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg_arch) ifndef $(target)_$(prop).$(bld_trg_arch) $(target)_$(prop).$(bld_trg_arch) = $$(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg_arch)) endif endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg_cpu) ifndef $(target)_$(prop).$(bld_trg_cpu) $(target)_$(prop).$(bld_trg_cpu) = $$(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg_cpu)) endif endif endef ## Inherit one acculumlative template property where the 'most significant' items are at the left end. # @param $(prop) Property name # @param $(target) Target name define def_inherit_template_one_accumulate_l ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop) ifeq ($$(flavor $(target)_$(prop)),simple) $$(evalcall2 def_simple_2_recursive,$(target)_$(prop)) endif $(target)_$(prop) += $$(TEMPLATE_$($(target)_TEMPLATE)_$(prop)) endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(KBUILD_TYPE) ifeq ($$(flavor $(target)_$(prop).$(KBUILD_TYPE)),simple) $$(evalcall2 def_simple_2_recursive,$(target)_$(prop).$(KBUILD_TYPE)) endif $(target)_$(prop).$(KBUILD_TYPE) += $$(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(KBUILD_TYPE)) endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg) ifeq ($$(flavor $(target)_$(prop).$(bld_trg)),simple) $$(evalcall2 def_simple_2_recursive,$(target)_$(prop).$(bld_trg)) endif $(target)_$(prop).$(bld_trg) += $$(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg)) endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg).$(bld_trg_arch) ifeq ($$(flavor $(target)_$(prop).$(bld_trg).$(bld_trg_arch)),simple) $$(evalcall2 def_simple_2_recursive,$(target)_$(prop).$(bld_trg).$(bld_trg_arch)) endif $(target)_$(prop).$(bld_trg).$(bld_trg_arch) += $$(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg).$(bld_trg_arch)) endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg_cpu) ifeq ($$(flavor $(target)_$(prop).$(bld_trg_cpu)),simple) $$(evalcall2 def_simple_2_recursive,$(target)_$(prop).$(bld_trg_cpu)) endif $(target)_$(prop).$(bld_trg_cpu) += $$(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg_cpu)) endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg_arch) ifeq ($$(flavor $(target)_$(prop).$(bld_trg_arch)),simple) $$(evalcall2 def_simple_2_recursive,$(target)_$(prop).$(bld_trg_arch)) endif $(target)_$(prop).$(bld_trg_arch) += $$(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg_arch)) endif endef ## Inherit one acculumlative template property where the 'most significant' items are at the right end. # @param $(prop) Property name # @param $(target) Target name define def_inherit_template_one_accumulate_r ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop) ifeq ($$(flavor $(target)_$(prop)),simple) $$(evalcall2 def_simple_2_recursive,$(target)_$(prop)) endif $(target)_$(prop) <=$$(TEMPLATE_$($(target)_TEMPLATE)_$(prop)) endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(KBUILD_TYPE) ifeq ($$(flavor $(target)_$(prop).$(KBUILD_TYPE)),simple) $$(evalcall2 def_simple_2_recursive,$(target)_$(prop).$(KBUILD_TYPE)) endif $(target)_$(prop).$(KBUILD_TYPE) <=$$(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(KBUILD_TYPE)) endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg) ifeq ($$(flavor $(target)_$(prop).$(bld_trg)),simple) $$(evalcall2 def_simple_2_recursive,$(target)_$(prop).$(bld_trg)) endif $(target)_$(prop).$(bld_trg) <=$$(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg)) endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg).$(bld_trg_arch) ifeq ($$(flavor $(target)_$(prop).$(bld_trg).$(bld_trg_arch)),simple) $$(evalcall2 def_simple_2_recursive,$(target)_$(prop).$(bld_trg).$(bld_trg_arch)) endif $(target)_$(prop).$(bld_trg).$(bld_trg_arch) <=$$(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg).$(bld_trg_arch)) endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg_cpu) ifeq ($$(flavor $(target)_$(prop).$(bld_trg_cpu)),simple) $$(evalcall2 def_simple_2_recursive,$(target)_$(prop).$(bld_trg_cpu)) endif $(target)_$(prop).$(bld_trg_cpu) <=$$(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg_cpu)) endif ifdef TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg_arch) ifeq ($$(flavor $(target)_$(prop).$(bld_trg_arch)),simple) $$(evalcall2 def_simple_2_recursive,$(target)_$(prop).$(bld_trg_arch)) endif $(target)_$(prop).$(bld_trg_arch) <=$$(TEMPLATE_$($(target)_TEMPLATE)_$(prop).$(bld_trg_arch)) endif endef ## Inherit template properties for on target. # @param $(target) Target name. define def_inherit_template # sanity check. ifdef _$(target)_ALREADY_PROCESSED $(error kBuild: The target $(target) appears more than once in the target lists! Please correct the makefile(s)) endif _$(target)_ALREADY_PROCESSED := 1 # Inherit any default template. ifdef TEMPLATE ifeq ($($(target)_TEMPLATE),) $(eval $(target)_TEMPLATE:=$(TEMPLATE)) endif endif # Expand the template if specified. ifneq ($($(target)_TEMPLATE),) $(foreach prop,$(PROPS_SINGLE),$(evalval def_inherit_template_one)) $(foreach prop,$(PROPS_DEFERRED),$(eval $(def_inherit_template_one_deferred))) # exploits the 2 evaluation, so no value! $(foreach prop,$(PROPS_ACCUMULATE_L),$(eval $(def_inherit_template_one_accumulate_l))) # += works fine without value $(foreach prop,$(PROPS_ACCUMULATE_R),$(eval $(def_inherit_template_one_accumulate_r))) # use <= (kmk addition) endif endef Invoked like this: $(kb-exp-tmpl 1,$(_ALL_TARGET_TARGETS),$(KBUILD_TARGET),$(KBUILD_TARGET_ARCH),$(KBUILD_TARGET_CPU),$(KBUILD_TYPE)) */ char * func_kbuild_expand_template(char *o, char **argv, const char *pszFuncName) { const char *pszVersion = argv[0]; const char *pszBldTrg = argv[2]; const char *pszBldTrgArch = argv[3]; const char *pszBldTrgCpu = argv[4]; const char *pszBldType = argv[5]; size_t cchBldTrg = strlen(pszBldTrg); size_t cchBldTrgArch = strlen(pszBldTrgArch); size_t cchBldTrgCpu = strlen(pszBldTrgCpu); size_t cchBldType = strlen(pszBldType); size_t cchMaxBld = cchBldTrg + cchBldTrgArch + cchBldTrgCpu + cchBldType; /* too big, but so what. */ struct kbet_key { unsigned int cch; char *psz; } aKeys[6]; unsigned int const cKeys = 6; unsigned int iKey; struct variable *pDefTemplate; struct variable *pProps; struct kbet_prop { const char *pch; unsigned int cch; enum kbet_prop_enum { kPropSingle, kPropDeferred, kPropAccumulateL, kPropAccumulateR } enmType; } *paProps; unsigned int cProps; unsigned int iProp; size_t cchMaxProp; struct variable *pVarTrg; struct variable *pVarSrc; const char *pszIter; const char *pszTarget; unsigned int cchTarget; char *pszSrc = 0; char *pszSrcRef = 0; char *pszSrcBuf = 0; size_t cchSrcBuf = 0; char *pszTrg = 0; size_t cchTrg = 0; /* * Validate input. */ if (pszVersion[0] != '1' || pszVersion[1]) OSS(fatal, NULL, "%s: Unsupported version `%s'", pszFuncName, pszVersion); if (!cchBldTrg) OS(fatal, NULL, "%s: missing bldtrg", pszFuncName); if (!cchBldTrgArch) OS(fatal, NULL, "%s: missing bld_trg_arch", pszFuncName); if (!cchBldTrgCpu) OS(fatal, NULL, "%s: missing bld_trg_cpu", pszFuncName); if (!cchBldType) OS(fatal, NULL, "%s: missing bld_type", pszFuncName); /* * Prepare the keywords, prepending dots for quicker copying. * This allows for an inner loop when processing properties, saving code * at the expense of a few xmallocs. */ /* the first entry is empty. */ aKeys[0].cch = 0; aKeys[0].psz = NULL; /* .$(bld_type) */ aKeys[1].cch = cchBldType + 1; aKeys[1].psz = xmalloc (aKeys[1].cch + 1); aKeys[1].psz[0] = '.'; memcpy(aKeys[1].psz + 1, pszBldType, cchBldType + 1); /* .$(bld_trg) */ aKeys[2].cch = cchBldTrg + 1; aKeys[2].psz = xmalloc (aKeys[2].cch + 1); aKeys[2].psz[0] = '.'; memcpy(aKeys[2].psz + 1, pszBldTrg, cchBldTrg + 1); /* .$(bld_trg).$(bld_trg_arch) */ aKeys[3].cch = cchBldTrg + 1 + cchBldTrgArch + 1; aKeys[3].psz = xmalloc (aKeys[3].cch + 1); aKeys[3].psz[0] = '.'; memcpy(aKeys[3].psz + 1, pszBldTrg, cchBldTrg); aKeys[3].psz[1 + cchBldTrg] = '.'; memcpy(aKeys[3].psz + 1 + cchBldTrg + 1, pszBldTrgArch, cchBldTrgArch + 1); /* .$(bld_trg_cpu) */ aKeys[4].cch = cchBldTrgCpu + 1; aKeys[4].psz = xmalloc (aKeys[4].cch + 1); aKeys[4].psz[0] = '.'; memcpy(aKeys[4].psz + 1, pszBldTrgCpu, cchBldTrgCpu + 1); /* .$(bld_trg_arch) */ aKeys[5].cch = cchBldTrgArch + 1; aKeys[5].psz = xmalloc (aKeys[5].cch + 1); aKeys[5].psz[0] = '.'; memcpy(aKeys[5].psz + 1, pszBldTrgArch, cchBldTrgArch + 1); /* * Prepare the properties, folding them into an array. * This way we won't have to reparse them for each an every target, though * it comes at the expense of one or more heap calls. */ #define PROP_ALLOC_INC 128 iProp = 0; cProps = PROP_ALLOC_INC; paProps = xmalloc(sizeof(*pProps) * cProps); pProps = kbuild_get_variable_n(ST("PROPS_SINGLE")); pszIter = pProps->value; while ((paProps[iProp].pch = find_next_token(&pszIter, &paProps[iProp].cch))) { paProps[iProp].enmType = kPropSingle; if (++iProp >= cProps) { cProps += PROP_ALLOC_INC; paProps = xrealloc(paProps, sizeof(*paProps) * cProps); } } pProps = kbuild_get_variable_n(ST("PROPS_DEFERRED")); pszIter = pProps->value; while ((paProps[iProp].pch = find_next_token(&pszIter, &paProps[iProp].cch))) { paProps[iProp].enmType = kPropDeferred; if (++iProp >= cProps) { cProps += PROP_ALLOC_INC; paProps = xrealloc(paProps, sizeof(*paProps) * cProps); } } pProps = kbuild_get_variable_n(ST("PROPS_ACCUMULATE_L")); pszIter = pProps->value; while ((paProps[iProp].pch = find_next_token(&pszIter, &paProps[iProp].cch))) { paProps[iProp].enmType = kPropAccumulateL; if (++iProp >= cProps) { cProps += PROP_ALLOC_INC; paProps = xrealloc(paProps, sizeof(*paProps) * cProps); } } pProps = kbuild_get_variable_n(ST("PROPS_ACCUMULATE_R")); pszIter = pProps->value; while ((paProps[iProp].pch = find_next_token(&pszIter, &paProps[iProp].cch))) { paProps[iProp].enmType = kPropAccumulateR; if (++iProp >= cProps) { cProps += PROP_ALLOC_INC; paProps = xrealloc(paProps, sizeof(*paProps) * cProps); } } #undef PROP_ALLOC_INC cProps = iProp; /* find the max prop length. */ cchMaxProp = paProps[0].cch; while (--iProp > 0) if (paProps[iProp].cch > cchMaxProp) cchMaxProp = paProps[iProp].cch; /* * Query and prepare (strip) the default template * (given by the TEMPLATE variable). */ pDefTemplate = kbuild_lookup_variable_n(ST("TEMPLATE")); if (pDefTemplate) { if ( pDefTemplate->value_length && ( ISSPACE(pDefTemplate->value[0]) || ISSPACE(pDefTemplate->value[pDefTemplate->value_length - 1]))) { unsigned int off; if (pDefTemplate->rdonly_val) OS(fatal, NULL, "%s: TEMPLATE is read-only", pszFuncName); /* head */ for (off = 0; ISSPACE(pDefTemplate->value[off]); off++) /* nothing */; if (off) { pDefTemplate->value_length -= off; memmove(pDefTemplate->value, pDefTemplate->value + off, pDefTemplate->value_length + 1); } /* tail */ off = pDefTemplate->value_length; while (off > 0 && ISSPACE(pDefTemplate->value[off - 1])) off--; pDefTemplate->value_length = off; pDefTemplate->value[off] = '\0'; VARIABLE_CHANGED(pDefTemplate); } if (!pDefTemplate->value_length) pDefTemplate = NULL; } /* * Iterate the target list. */ pszIter = argv[1]; while ((pszTarget = find_next_token(&pszIter, &cchTarget))) { char *pszTrgProp, *pszSrcProp; char *pszTrgKey, *pszSrcKey; struct variable *pTmpl; const char *pszTmpl; size_t cchTmpl, cchMax; /* resize the target buffer. */ cchMax = cchTarget + cchMaxProp + cchMaxBld + 10; if (cchTrg < cchMax) { cchTrg = (cchMax + 31U) & ~(size_t)31; pszTrg = xrealloc(pszTrg, cchTrg); } /* * Query the TEMPLATE property, if not found or zero-length fall back on the default. */ memcpy(pszTrg, pszTarget, cchTarget); pszTrgProp = pszTrg + cchTarget; memcpy(pszTrgProp, "_TEMPLATE", sizeof("_TEMPLATE")); pszTrgProp++; /* after '_'. */ /** @todo Change this to a recursive lookup with simplification below. That * will allow target_TEMPLATE = $(NO_SUCH_TEMPLATE) instead of having * to use target_TEMPLATE = DUMMY */ pTmpl = kbuild_lookup_variable_n(pszTrg, cchTarget + sizeof("_TEMPLATE") - 1); if (!pTmpl || !pTmpl->value_length) { if (!pDefTemplate) continue; /* no template */ pszTmpl = pDefTemplate->value; cchTmpl = pDefTemplate->value_length; } else { pszTmpl = pTmpl->value; cchTmpl = pTmpl->value_length; while (ISSPACE(*pszTmpl)) cchTmpl--, pszTmpl++; if (!cchTmpl) continue; /* no template */ } /* resize the source buffer. */ cchMax = sizeof("TEMPLATE_") + cchTmpl + cchMaxProp + cchMaxBld + 10 + sizeof(void *); if (cchSrcBuf < cchMax) { cchSrcBuf = (cchMax + 31U) & ~(size_t)31; pszSrcBuf = xrealloc(pszSrcBuf, cchSrcBuf); pszSrc = pszSrcBuf + sizeof(void *); assert(sizeof(void *) >= 2); pszSrcRef = pszSrc - 2; pszSrcRef[0] = '$'; pszSrcRef[1] = '('; } /* prepare the source buffer */ memcpy(pszSrc, "TEMPLATE_", sizeof("TEMPLATE_") - 1); pszSrcProp = pszSrc + sizeof("TEMPLATE_") - 1; memcpy(pszSrcProp, pszTmpl, cchTmpl); pszSrcProp += cchTmpl; *pszSrcProp++ = '_'; /* * Process properties. * Note! The single and deferred are handled in the same way now. */ #define BY_REF_LIMIT 64 /*(cchSrcVar * 4 > 64 ? cchSrcVar * 4 : 64)*/ for (iProp = 0; iProp < cProps; iProp++) { memcpy(pszTrgProp, paProps[iProp].pch, paProps[iProp].cch); pszTrgKey = pszTrgProp + paProps[iProp].cch; memcpy(pszSrcProp, paProps[iProp].pch, paProps[iProp].cch); pszSrcKey = pszSrcProp + paProps[iProp].cch; for (iKey = 0; iKey < cKeys; iKey++) { char *pszTrgEnd; size_t cchSrcVar; /* lookup source, skip ahead if it doesn't exist. */ memcpy(pszSrcKey, aKeys[iKey].psz, aKeys[iKey].cch); cchSrcVar = pszSrcKey - pszSrc + aKeys[iKey].cch; pszSrc[cchSrcVar] = '\0'; pVarSrc = kbuild_query_recursive_variable_n(pszSrc, cchSrcVar); if (!pVarSrc) continue; /* lookup target, skip ahead if it exists. */ memcpy(pszTrgKey, aKeys[iKey].psz, aKeys[iKey].cch); pszTrgEnd = pszTrgKey + aKeys[iKey].cch; *pszTrgEnd = '\0'; pVarTrg = kbuild_query_recursive_variable_n(pszTrg, pszTrgEnd - pszTrg); switch (paProps[iProp].enmType) { case kPropAccumulateL: case kPropAccumulateR: if (pVarTrg) { /* Append to existing variable. If the source is recursive, or we append by reference, we'll have to make sure the target is recusive as well. */ if ( !pVarTrg->recursive && ( pVarSrc->value_length >= BY_REF_LIMIT || pVarSrc->recursive)) pVarTrg->recursive = 1; if (pVarSrc->value_length < BY_REF_LIMIT) append_string_to_variable(pVarTrg, pVarSrc->value, pVarSrc->value_length, paProps[iProp].enmType == kPropAccumulateL /* append */); else { pszSrc[cchSrcVar] = ')'; pszSrc[cchSrcVar + 1] = '\0'; append_string_to_variable(pVarTrg, pszSrcRef, 2 + cchSrcVar + 1, paProps[iProp].enmType == kPropAccumulateL /* append */); } break; } /* else: the target variable doesn't exist, create it. */ /* fall thru */ case kPropSingle: case kPropDeferred: if (pVarTrg) continue; /* skip ahead if it already exists. */ /* copy the variable if its short, otherwise reference it. */ if (pVarSrc->value_length < BY_REF_LIMIT) define_variable_vl_global(pszTrg, pszTrgEnd - pszTrg, pVarSrc->value, pVarSrc->value_length, 1 /* duplicate_value */, o_file, pVarSrc->recursive, NULL /* flocp */); else { pszSrc[cchSrcVar] = ')'; pszSrc[cchSrcVar + 1] = '\0'; define_variable_vl_global(pszTrg, pszTrgEnd - pszTrg, pszSrcRef, 2 + cchSrcVar + 1, 1 /* duplicate_value */, o_file, 1 /* recursive */, NULL /* flocp */); } break; } } /* foreach key */ } /* foreach prop */ #undef BY_REF_LIMIT } /* foreach target */ /* * Cleanup. */ free(pszSrcBuf); free(pszTrg); free(paProps); for (iKey = 1; iKey < cKeys; iKey++) free(aKeys[iKey].psz); return o; } #endif /* KMK_HELPERS */