diff options
Diffstat (limited to 'src/kmk/kbuild.c')
-rw-r--r-- | src/kmk/kbuild.c | 3030 |
1 files changed, 3030 insertions, 0 deletions
diff --git a/src/kmk/kbuild.c b/src/kmk/kbuild.c new file mode 100644 index 0000000..56a0b28 --- /dev/null +++ b/src/kmk/kbuild.c @@ -0,0 +1,3030 @@ +/* $Id: kbuild.c 3425 2020-08-21 12:45:06Z bird $ */ +/** @file + * kBuild specific make functionality. + */ + +/* + * Copyright (c) 2006-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net> + * + * This file is part of kBuild. + * + * kBuild is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * kBuild is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kBuild. If not, see <http://www.gnu.org/licenses/> + * + */ + +/* No GNU coding style here! */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#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 <Windows.h> +#endif +#if defined(__APPLE__) +# include <mach-o/dyld.h> +#endif +#if defined(__FreeBSD__) +# include <dlfcn.h> +# include <sys/link_elf.h> +#endif + +#include "kbuild.h" +#include "k/kDefs.h" + +#include <assert.h> + + +/******************************************************************************* +* 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); \ + \ + /* <lead>.$(bld_trg).$(bld_trg_arch) */ \ + if (!pVar) \ + pVar = kbuild_lookup_not_empty_variable_n(pszBuf, psz4 - pszBuf); \ + \ + /* <lead>.$(bld_trg).$(bld_type) */ \ + if (!pVar) \ + { \ + psz = psz3 + 1; \ + ADD_VAR(pBldType); \ + pVar = kbuild_lookup_not_empty_variable_n(pszBuf, psz - pszBuf); \ + } \ + \ + /* <lead>.$(bld_trg_arch) */ \ + if (!pVar) \ + { \ + psz = psz2 + 1; \ + ADD_VAR(pBldTrgArch); \ + pVar = kbuild_lookup_not_empty_variable_n(pszBuf, psz - pszBuf); \ + } \ + \ + /* <lead>.$(bld_trg) */ \ + if (!pVar) \ + { \ + psz = psz2 + 1; \ + ADD_VAR(pBldTrg); \ + pVar = kbuild_lookup_not_empty_variable_n(pszBuf, psz - pszBuf); \ + } \ + \ + /* <lead>.$(bld_type) */ \ + if (!pVar) \ + { \ + psz = psz2 + 1; \ + ADD_VAR(pBldType); \ + pVar = kbuild_lookup_not_empty_variable_n(pszBuf, psz - pszBuf); \ + } \ + } \ + else \ + { \ + /* <lead>.$(bld_trg).$(bld_trg_arch) */ \ + pVar = kbuild_lookup_not_empty_variable_n(pszBuf, psz4 - pszBuf); \ + \ + /* <lead>.$(bld_trg) */ \ + if (!pVar) \ + pVar = kbuild_lookup_not_empty_variable_n(pszBuf, psz3 - pszBuf); \ + } \ + \ + /* <lead> */ \ + 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 <varname> [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 <varname> [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 <var> [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 <prop> <var> <dir> [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, "<source>_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 */ + |