Logo Search packages:      
Sourcecode: kbuild version File versions  Download package

kbuild.c

Go to the documentation of this file.
/* $Id: kbuild.c 1503 2008-04-08 23:16:53Z bird $ */
/** @file
 *
 * kBuild specific make functionality.
 *
 * Copyright (c) 2006-2007 knut st. osmundsen <bird-kBuild-spam@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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/* No GNU coding style here! */


/*******************************************************************************
*   Header Files                                                               *
*******************************************************************************/
#include "make.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

#include "kbuild.h"

#include <assert.h>
#include <stdarg.h>
#ifndef va_copy
# define va_copy(dst, src) do {(dst) = (src);} while (0)
#endif


/*******************************************************************************
*   Internal Functions                                                         *
*******************************************************************************/
/* function.c */
char * abspath(const char *name, char *apath);

/*******************************************************************************
*   Global Variables                                                           *
*******************************************************************************/
/** The argv[0] passed to main. */
00065 static const char *g_pszExeName;
/** The initial working directory. */
00067 static char *g_pszInitialCwd;


/**
 * Initialize kBuild stuff.
 *
 * @param   argc    Number of arguments to main().
 * @param   argv    The main() argument vector.
 */
00076 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
        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;
    else
        szTmp[rc] = '\0';

#elif defined(__gnu_linux__) /** @todo find proper define... */
    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/%d/path/a.out", 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);
}


/**
 * Wrapper that ensures correct starting_directory.
 */
00193 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).
 */
00211 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))
        {
            const char *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))
                    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).
 */
00248 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))
        {
            const char *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))
                    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).
 */
00298 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, int *pcch, int *pcchAlloc, int fCanFree)
{
    const char *pszIterator;
    const char *pszInCur;
    unsigned int cchInCur;
    unsigned int cRelativePaths;

    /*
     * The first pass, count the relative paths.
     */
    cRelativePaths = 0;
    pszIterator = *ppsz;
    while ((pszInCur = find_next_token(&pszIterator, &cchInCur)))
    {
        /* is relative? */
#ifdef HAVE_DOS_PATHS
        if (pszInCur[0] != '/' && pszInCur[0] != '\\' && (cchInCur < 2 || pszInCur[1] != ':'))
#else
        if (pszInCur[0] != '/')
#endif
            cRelativePaths++;
    }

    /*
     * The second pass construct the new string.
     */
    if (cRelativePaths)
    {
        const size_t cchOut = *pcch + cRelativePaths * (pDefPath->value_length + 1) + 1;
        char *pszOut = xmalloc(cchOut);
        char *pszOutCur = pszOut;
        const char *pszInNextCopy = *ppsz;

        cRelativePaths = 0;
        pszIterator = *ppsz;
        while ((pszInCur = find_next_token(&pszIterator, &cchInCur)))
        {
            /* is relative? */
#ifdef HAVE_DOS_PATHS
            if (pszInCur[0] != '/' && pszInCur[0] != '\\' && (cchInCur < 2 || pszInCur[1] != ':'))
#else
            if (pszInCur[0] != '/')
#endif
            {
                PATH_VAR(szAbsPathIn);
                PATH_VAR(szAbsPathOut);

                if (pDefPath->value_length + cchInCur + 1 >= GET_PATH_MAX)
                    continue;

                /* Create the abspath input. */
                memcpy(szAbsPathIn, pDefPath->value, pDefPath->value_length);
                szAbsPathIn[pDefPath->value_length] = '/';
                memcpy(&szAbsPathIn[pDefPath->value_length + 1], pszInCur, cchInCur);
                szAbsPathIn[pDefPath->value_length + 1 + cchInCur] = '\0';

                if (abspath(szAbsPathIn, szAbsPathOut) != NULL)
                {
                    const size_t cchAbsPathOut = strlen(szAbsPathOut);
                    assert(cchAbsPathOut <= pDefPath->value_length + 1 + cchInCur);

                    /* 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. */
                    memcpy(pszOutCur, szAbsPathOut, cchAbsPathOut);
                    pszOutCur += cchAbsPathOut;
                }
            }
        }
        /* 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.
 */
static struct variable *
kbuild_get_variable(const char *pszName)
{
#ifndef NDEBUG
    unsigned i;
#endif
    struct variable *pVar = lookup_variable(pszName, strlen(pszName));
    if (!pVar)
        fatal(NILF, _("variable `%s' isn't defined!"), pszName);
    if (pVar->recursive)
        fatal(NILF, _("variable `%s' is defined as `recursive' instead of `simple'!"), pszName);
#ifndef NDEBUG
    i = strlen(pVar->value);
    if (i != pVar->value_length)
    {
        printf("%d != %d %s\n", pVar->value_length, i, pVar->name);
# ifdef _MSC_VER
        __debugbreak();
# endif
        assert(0);
    }
#endif
    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)
{
#ifndef NDEBUG
    unsigned i;
#endif
    struct variable *pVar = lookup_variable(pszName, strlen(pszName));
    if (!pVar)
        fatal(NILF, _("variable `%s' isn't defined!"), pszName);
#ifndef NDEBUG
    i = strlen(pVar->value);
    if (i != pVar->value_length)
    {
        printf("%d != %d %s\n", pVar->value_length, i, pVar->name);
# ifdef _MSC_VER
        __debugbreak();
# endif
        assert(0);
    }
#endif
    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)
{
#ifndef NDEBUG
    unsigned i;
#endif
    struct variable *pVar = lookup_variable(pszName, strlen(pszName));
    if (pVar)
    {
#ifndef NDEBUG
        i = strlen(pVar->value);
        if (i != pVar->value_length)
        {
            printf("%d != %d %s\n", pVar->value_length, i, pVar->name);
# ifdef _MSC_VER
            __debugbreak();
# endif
            assert(0);
        }
#endif
    }
    return pVar;
}


/**
 * 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))
    {
        char *pszExpanded = allocated_variable_expand(pVar->value);
        free(pVar->value);
        pVar->value = pszExpanded;
        pVar->value_length = strlen(pVar->value);
        pVar->value_alloc_len = pVar->value_length + 1;
    }
    pVar->recursive = 0;
    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.
 */
static struct variable *
kbuild_lookup_variable(const char *pszName)
{
    struct variable *pVar = lookup_variable(pszName, strlen(pszName));
    if (pVar)
    {
#ifndef NDEBUG
        unsigned i = strlen(pVar->value);
        if (i != pVar->value_length)
        {
            printf("%d != %d %s\n", pVar->value_length, i, pVar->name);
# ifdef _MSC_VER
            __debugbreak();
# endif
            assert(0);
        }
#endif
        /* Make sure the variable is simple, convert it if necessary. */
        if (pVar->recursive)
            kbuild_simplify_variable(pVar);
    }
    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.
 */
static struct variable *
kbuild_lookup_variable_defpath(struct variable *pDefPath, const char *pszName)
{
    struct variable *pVar = kbuild_lookup_variable(pszName);
    if (pVar && pDefPath)
        kbuild_apply_defpath(pDefPath, &pVar->value, &pVar->value_length, &pVar->value_alloc_len, 1);
    return pVar;
}


/** Same as kbuild_lookup_variable except that a '%s' in the name string
 * will be substituted with the values of the variables in the va list. */
static struct variable *
kbuild_lookup_variable_fmt_va(struct variable *pDefPath, const char *pszNameFmt, va_list va)
{
    va_list va2;
    size_t cchName;
    const char *pszFmt;
    char *pszName;
    char *psz;

    /* first pass, calc value name size and allocate stack buffer. */
    va_copy(va2, va);

    cchName = strlen(pszNameFmt) + 1;
    pszFmt = strchr(pszNameFmt, '%');
    while (pszFmt)
    {
        struct variable *pVar = va_arg(va, struct variable *);
        if (pVar)
            cchName += pVar->value_length;
        pszFmt = strchr(pszFmt + 1, '%');
    }
    pszName = alloca(cchName);

    /* second pass format it. */
    pszFmt = pszNameFmt;
    psz = pszName;
    for (;;)
    {
        char ch = *pszFmt++;
        if (ch != '%')
        {
            *psz++ = ch;
            if (!ch)
                break;
        }
        else
        {
            struct variable *pVar = va_arg(va2, struct variable *);
            if (pVar)
            {
                memcpy(psz, pVar->value, pVar->value_length);
                psz += pVar->value_length;
            }
        }
    }
    va_end(va2);

    if (pDefPath)
        return kbuild_lookup_variable_defpath(pDefPath, pszName);
    return kbuild_lookup_variable(pszName);
}


/** Same as kbuild_lookup_variable except that a '%s' in the name string
 * will be substituted with the values of the variables in the ellipsis.  */
static struct variable *
kbuild_lookup_variable_fmt(struct variable *pDefPath, const char *pszNameFmt, ...)
{
    struct variable *pVar;
    va_list va;
    va_start(va, pszNameFmt);
    pVar = kbuild_lookup_variable_fmt_va(pDefPath, pszNameFmt, va);
    va_end(va);
    return pVar;
}


/**
 * Gets the first defined property variable.
 */
static struct variable *
kbuild_first_prop(struct variable *pTarget, struct variable *pSource,
                  struct variable *pTool, struct variable *pType,
                  struct variable *pBldTrg, struct variable *pBldTrgArch,
                  const char *pszPropF1, const char *pszPropF2, const char *pszVarName)
{
    struct variable *pVar;
    struct variable PropF1, PropF2;

    PropF1.value = (char *)pszPropF1;
    PropF1.value_length = strlen(pszPropF1);

    PropF2.value = (char *)pszPropF2;
    PropF2.value_length = strlen(pszPropF2);

    if (    (pVar = kbuild_lookup_variable_fmt(NULL, "%_%_%%.%.%",pTarget, pSource, pType, &PropF2, pBldTrg, pBldTrgArch))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%_%%.%",  pTarget, pSource, pType, &PropF2, pBldTrg))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%_%%",    pTarget, pSource, pType, &PropF2))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%_%.%.%", pTarget, pSource, &PropF2, pBldTrg, pBldTrgArch))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%_%.%",   pTarget, pSource, &PropF2, pBldTrg))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%_%",     pTarget, pSource, &PropF2))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%%.%.%",  pSource, pType, &PropF2, pBldTrg, pBldTrgArch))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%%.%",    pSource, pType, &PropF2, pBldTrg))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%%",      pSource, pType, &PropF2))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%.%.%",   pSource, &PropF2, pBldTrg, pBldTrgArch))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%.%",     pSource, &PropF2, pBldTrg))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%",       pSource, &PropF2))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%%.%.%",  pTarget, pType, &PropF2, pBldTrg, pBldTrgArch))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%%.%",    pTarget, pType, &PropF2, pBldTrg))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%%",      pTarget, pType, &PropF2))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%.%.%",   pTarget, &PropF2, pBldTrg, pBldTrgArch))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%.%",     pTarget, &PropF2, pBldTrg))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%_%",       pTarget, &PropF2))

        ||  (pTool && (pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%%.%.%",   pTool, pType, &PropF2, pBldTrg, pBldTrgArch)))
        ||  (pTool && (pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%%.%",     pTool, pType, &PropF2, pBldTrg)))
        ||  (pTool && (pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%%",       pTool, pType, &PropF2)))
        ||  (pTool && (pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%.%.%",    pTool, &PropF2, pBldTrg, pBldTrgArch)))
        ||  (pTool && (pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%.%",      pTool, &PropF2, pBldTrg)))
        ||  (pTool && (pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%",        pTool, &PropF2)))

        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%%.%.%",    pType, &PropF1, pBldTrg, pBldTrgArch))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%%.%",      pType, &PropF1, pBldTrg))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%%",        pType, &PropF1))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%.%.%",     &PropF1, pBldTrg, pBldTrgArch))
        ||  (pVar = kbuild_lookup_variable_fmt(NULL, "%.%",       &PropF1, pBldTrg))
        ||  (pVar = kbuild_lookup_variable(pszPropF1))
       )
    {
        /* strip it */
        char *psz = pVar->value;
        char *pszEnd = psz + pVar->value_length;
        while (isblank((unsigned char)*psz))
            psz++;
        while (pszEnd > psz && isblank((unsigned char)pszEnd[-1]))
            pszEnd--;
        if (pszEnd > psz)
        {
            char chSaved = *pszEnd;
            *pszEnd = '\0';
            pVar = define_variable_vl(pszVarName, strlen(pszVarName), psz, pszEnd - psz,
                                      1 /* duplicate */, o_file, 0 /* !recursive */);
            *pszEnd = chSaved;
            if (pVar)
                return pVar;
        }
    }
    return NULL;
}


/*
_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) \
    $($(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, const char *pszVarName)
{
    struct variable *pVar = kbuild_first_prop(pTarget, pSource, NULL, pType, pBldTrg, pBldTrgArch,
                                              "TOOL", "TOOL", pszVarName);
    if (!pVar)
        fatal(NILF, _("no tool for source `%s' in target `%s'!"), pSource->value, pTarget->value);
    return pVar;
}


/* Implements _SOURCE_TOOL. */
char *
func_kbuild_source_tool(char *o, char **argv, const char *pszFuncName)
{
    struct variable *pVar = kbuild_get_source_tool(kbuild_get_variable("target"),
                                                   kbuild_get_variable("source"),
                                                   kbuild_get_variable("type"),
                                                   kbuild_get_variable("bld_trg"),
                                                   kbuild_get_variable("bld_trg_arch"),
                                                   argv[0]);
    if (pVar)
         o = variable_buffer_output(o, pVar->value, pVar->value_length);
    return o;

}


/* This has been extended a bit, it's now identical to _SOURCE_TOOL.
$(firstword \
      $($(target)_$(source)_OBJSUFF.$(bld_trg).$(bld_trg_arch))\
      $($(target)_$(source)_OBJSUFF.$(bld_trg))\
      $($(target)_$(source)_OBJSUFF)\
      $($(source)_OBJSUFF.$(bld_trg).$(bld_trg_arch))\
      $($(source)_OBJSUFF.$(bld_trg))\
      $($(source)_OBJSUFF)\
      $($(target)_OBJSUFF.$(bld_trg).$(bld_trg_arch))\
      $($(target)_OBJSUFF.$(bld_trg))\
      $($(target)_OBJSUFF)\
      $(TOOL_$(tool)_$(type)OBJSUFF.$(bld_trg).$(bld_trg_arch))\
      $(TOOL_$(tool)_$(type)OBJSUFF.$(bld_trg))\
      $(TOOL_$(tool)_$(type)OBJSUFF)\
      $(SUFF_OBJ))
*/
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, const char *pszVarName)
{
    struct variable *pVar = kbuild_first_prop(pTarget, pSource, pTool, pType, pBldTrg, pBldTrgArch,
                                              "SUFF_OBJ", "OBJSUFF", pszVarName);
    if (!pVar)
        fatal(NILF, _("no OBJSUFF attribute or SUFF_OBJ default for source `%s' in target `%s'!"), pSource->value, pTarget->value);
    return pVar;
}


/*  */
char *
func_kbuild_object_suffix(char *o, char **argv, const char *pszFuncName)
{
    struct variable *pVar = kbuild_get_object_suffix(kbuild_get_variable("target"),
                                                     kbuild_get_variable("source"),
                                                     kbuild_get_variable("tool"),
                                                     kbuild_get_variable("type"),
                                                     kbuild_get_variable("bld_trg"),
                                                     kbuild_get_variable("bld_trg_arch"),
                                                     argv[0]);
    if (pVar)
         o = variable_buffer_output(o, pVar->value, pVar->value_length);
    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("PATH_TARGET");
    struct variable *pPathRoot   = kbuild_get_variable("PATH_ROOT");
    struct variable *pPathSubCur = kbuild_get_variable("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 uncessary 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_file, 0 /* !recursive */);
}


/* Implements _OBJECT_BASE. */
char *
func_kbuild_object_base(char *o, char **argv, const char *pszFuncName)
{
    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);
    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;
};


/* 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)
{
    int i, j;
    size_t cchTmp, cch;
    char *pszTmp;
    unsigned cchCur;
    char *pszCur;
    const char *pszIterator;

    /* basic init. */
    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;
    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(pszTmp);
    while ((pszCur = find_next_token(&pszIterator, &cchCur)) != 0)
        pSdks->cGlobal++;
    i += pSdks->cGlobal;

    /* the target sdks.*/
    pSdks->iTarget = i;
    pSdks->cTarget = 0;
    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(pszTmp);
    while ((pszCur = find_next_token(&pszIterator, &cchCur)) != 0)
        pSdks->cTarget++;
    i += pSdks->cTarget;

    /* the source sdks.*/
    pSdks->iSource = i;
    pSdks->cSource = 0;
    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(pszTmp);
    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(pszTmp);
    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++)
    {
        int k = i;
        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';
}


/* 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);
}


/* 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, const char *pszVarName, int iDirection)
{
    struct variable *pVar;
    unsigned iSdk, iSdkEnd;
    int cVars, iVar, iVarEnd;
    size_t cchTotal;
    char *pszResult, *psz;
    struct
    {
        struct variable    *pVar;
        int                 cchExp;
        char               *pszExp;
    } *paVars;

    struct variable Prop = {0};
    Prop.value = (char *)pszProp;
    Prop.value_length = strlen(pszProp);

    assert(iDirection == 1 || iDirection == -1);

    /*
     * Get the variables.
     */
    cVars = 12 * (pSdks->c + 5);
    paVars = alloca(cVars * sizeof(paVars[0]));

    iVar = 0;
    /* the tool (lowest priority) */
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%",      pTool, &Prop);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%.%",    pTool, &Prop, pBldType);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%.%",    pTool, &Prop, pBldTrg);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%.%",    pTool, &Prop, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%.%.%",  pTool, &Prop, pBldTrg, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%.%",    pTool, &Prop, pBldTrgCpu);

    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%%",     pTool, pType, &Prop);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%%.%",   pTool, pType, &Prop, pBldType);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%%.%",   pTool, pType, &Prop, pBldTrg);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%%.%",   pTool, pType, &Prop, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%%.%.%", pTool, pType, &Prop, pBldTrg, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "TOOL_%_%%.%",   pTool, pType, &Prop, pBldTrgCpu);

    /* 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];
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%",      pSdk, &Prop);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%",    pSdk, &Prop, pBldType);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%",    pSdk, &Prop, pBldTrg);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%",    pSdk, &Prop, pBldTrgArch);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%.%",  pSdk, &Prop, pBldTrg, pBldTrgArch);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%",    pSdk, &Prop, pBldTrgCpu);

        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%",     pSdk, pType, &Prop);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%",   pSdk, pType, &Prop, pBldType);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%",   pSdk, pType, &Prop, pBldTrg);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%",   pSdk, pType, &Prop, pBldTrgArch);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%.%", pSdk, pType, &Prop, pBldTrg, pBldTrgArch);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%",   pSdk, pType, &Prop, pBldTrgCpu);
    }

    /* the globals */
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "%",      &Prop);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "%.%",    &Prop, pBldType);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "%.%",    &Prop, pBldTrg);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "%.%",    &Prop, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "%.%.%",  &Prop, pBldTrg, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "%.%",    &Prop, pBldTrgCpu);

    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "%%",     pType, &Prop);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "%%.%",   pType, &Prop, pBldType);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "%%.%",   pType, &Prop, pBldTrg);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "%%.%",   pType, &Prop, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "%%.%.%", pType, &Prop, pBldTrg, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "%%.%",   pType, &Prop, pBldTrgCpu);

    /* 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];
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%",      pSdk, &Prop);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%",    pSdk, &Prop, pBldType);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%",    pSdk, &Prop, pBldTrg);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%",    pSdk, &Prop, pBldTrgArch);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%.%",  pSdk, &Prop, pBldTrg, pBldTrgArch);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%",    pSdk, &Prop, pBldTrgCpu);

        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%",     pSdk, pType, &Prop);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%",   pSdk, pType, &Prop, pBldType);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%",   pSdk, pType, &Prop, pBldTrg);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%",   pSdk, pType, &Prop, pBldTrgArch);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%.%", pSdk, pType, &Prop, pBldTrg, pBldTrgArch);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%",   pSdk, pType, &Prop, pBldTrgCpu);
    }

    /* the target */
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%",      pTarget, &Prop);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%.%",    pTarget, &Prop, pBldType);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%.%",    pTarget, &Prop, pBldTrg);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%.%",    pTarget, &Prop, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%.%.%",  pTarget, &Prop, pBldTrg, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%.%",    pTarget, &Prop, pBldTrgCpu);

    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%%",     pTarget, pType, &Prop);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%%.%",   pTarget, pType, &Prop, pBldType);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%%.%",   pTarget, pType, &Prop, pBldTrg);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%%.%",   pTarget, pType, &Prop, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%%.%.%", pTarget, pType, &Prop, pBldTrg, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%%.%",   pTarget, pType, &Prop, pBldTrgCpu);

    /* 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];
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%",      pSdk, &Prop);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%",    pSdk, &Prop, pBldType);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%",    pSdk, &Prop, pBldTrg);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%",    pSdk, &Prop, pBldTrgArch);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%.%",  pSdk, &Prop, pBldTrg, pBldTrgArch);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%",    pSdk, &Prop, pBldTrgCpu);

        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%",     pSdk, pType, &Prop);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%",   pSdk, pType, &Prop, pBldType);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%",   pSdk, pType, &Prop, pBldTrg);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%",   pSdk, pType, &Prop, pBldTrgArch);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%.%", pSdk, pType, &Prop, pBldTrg, pBldTrgArch);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%",   pSdk, pType, &Prop, pBldTrgCpu);
    }

    /* the source */
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%",      pSource, &Prop);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%.%",    pSource, &Prop, pBldType);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%.%",    pSource, &Prop, pBldTrg);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%.%",    pSource, &Prop, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%.%.%",  pSource, &Prop, pBldTrg, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%.%",    pSource, &Prop, pBldTrgCpu);

    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%%",     pSource, pType, &Prop);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%%.%",   pSource, pType, &Prop, pBldType);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%%.%",   pSource, pType, &Prop, pBldTrg);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%%.%",   pSource, pType, &Prop, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%%.%.%", pSource, pType, &Prop, pBldTrg, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%%.%",   pSource, pType, &Prop, pBldTrgCpu);


    /* 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];
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%",      pSdk, &Prop);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%",    pSdk, &Prop, pBldType);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%",    pSdk, &Prop, pBldTrg);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%",    pSdk, &Prop, pBldTrgArch);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%.%",  pSdk, &Prop, pBldTrg, pBldTrgArch);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%.%",    pSdk, &Prop, pBldTrgCpu);

        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%",     pSdk, pType, &Prop);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%",   pSdk, pType, &Prop, pBldType);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%",   pSdk, pType, &Prop, pBldTrg);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%",   pSdk, pType, &Prop, pBldTrgArch);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%.%", pSdk, pType, &Prop, pBldTrg, pBldTrgArch);
        paVars[iVar++].pVar = kbuild_lookup_variable_fmt(NULL, "SDK_%_%%.%",   pSdk, pType, &Prop, pBldTrgCpu);
    }

    /* the target + source */
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%_%",      pTarget, pSource, &Prop);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%_%.%",    pTarget, pSource, &Prop, pBldType);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%_%.%",    pTarget, pSource, &Prop, pBldTrg);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%_%.%",    pTarget, pSource, &Prop, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%_%.%.%",  pTarget, pSource, &Prop, pBldTrg, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%_%.%",    pTarget, pSource, &Prop, pBldTrgCpu);

    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%_%%",     pTarget, pSource, pType, &Prop);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%_%%.%",   pTarget, pSource, pType, &Prop, pBldType);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%_%%.%",   pTarget, pSource, pType, &Prop, pBldTrg);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%_%%.%",   pTarget, pSource, pType, &Prop, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%_%%.%.%", pTarget, pSource, pType, &Prop, pBldTrg, pBldTrgArch);
    paVars[iVar++].pVar = kbuild_lookup_variable_fmt(pDefPath, "%_%_%%.%",   pTarget, pSource, pType, &Prop, pBldTrgCpu);

    assert(cVars == iVar);

    /*
     * Expand the variables and calculate the total length.
     */
    cchTotal = 0;
    iVarEnd = iDirection == 1 ? cVars : 0;
    for (iVar = iDirection == 1 ? 0 : cVars - 1; iVar != iVarEnd; iVar += iDirection)
    {
        paVars[iVar].cchExp = 0;
        if (!paVars[iVar].pVar)
            continue;
        if (    paVars[iVar].pVar->flavor == f_simple
            ||  !strchr(paVars[iVar].pVar->value, '$'))
        {
            paVars[iVar].pszExp = paVars[iVar].pVar->value;
            paVars[iVar].cchExp = paVars[iVar].pVar->value_length;
        }
        else
        {
            paVars[iVar].pszExp = allocated_variable_expand(paVars[iVar].pVar->value);
            paVars[iVar].cchExp = strlen(paVars[iVar].pszExp);
        }
        if (pDefPath)
            kbuild_apply_defpath(pDefPath, &paVars[iVar].pszExp, &paVars[iVar].cchExp, NULL,
                                 paVars[iVar].pszExp != paVars[iVar].pVar->value);
        cchTotal += paVars[iVar].cchExp + 1;
    }

    /*
     * Construct the result value.
     */
    psz = pszResult = xmalloc(cchTotal + 1);
    iVarEnd = iDirection == 1 ? cVars : 0;
    for (iVar = iDirection == 1 ? 0 : cVars - 1; iVar != iVarEnd; iVar += iDirection)
    {
        if (!paVars[iVar].cchExp)
            continue;
        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);
    }
    if (psz != pszResult)
        psz--;
    *psz = '\0';
    cchTotal = psz - pszResult;

    pVar = define_variable_vl(pszVarName, strlen(pszVarName), pszResult, cchTotal,
                              0 /* take pszResult */ , o_file, 0 /* !recursive */);
    return pVar;
}


/* get a source property. Doesn't respect the default path. */
char *
func_kbuild_source_prop(char *o, char **argv, const char *pszFuncName)
{
    struct variable *pTarget = kbuild_get_variable("target");
    struct variable *pSource = kbuild_get_variable("source");
    struct variable *pDefPath = NULL;
    struct variable *pType = kbuild_get_variable("type");
    struct variable *pTool = kbuild_get_variable("tool");
    struct variable *pBldType = kbuild_get_variable("bld_type");
    struct variable *pBldTrg = kbuild_get_variable("bld_trg");
    struct variable *pBldTrgArch = kbuild_get_variable("bld_trg_arch");
    struct variable *pBldTrgCpu = kbuild_get_variable("bld_trg_cpu");
    struct variable *pVar;
    struct kbuild_sdks Sdks;
    int iDirection;
    if (!strcmp(argv[2], "left-to-right"))
        iDirection = 1;
    else if (!strcmp(argv[2], "right-to-left"))
        iDirection = -1;
    else
        fatal(NILF, _("incorrect direction argument `%s'!"), argv[2]);
    if (argv[3])
        pDefPath = kbuild_get_variable("defpath");

    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], argv[1], iDirection);
    if (pVar)
         o = variable_buffer_output(o, pVar->value, pVar->value_length);

    kbuild_put_sdks(&Sdks);
    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("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_file, 0 /* !recursive */);

    /*
     * obj
     */
    *psz = '\0';
    pObj = define_variable_vl(pszVarName, strlen(pszVarName), pszResult, psz - pszResult,
                              1/* dup */, o_file, 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)
            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_file, 0 /* !recursive */);

    return pObj;
}


/* setup the base variables for def_target_source_c_cpp_asm_new:

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)
*/
char *
func_kbuild_source_one(char *o, char **argv, const char *pszFuncName)
{
    static int s_fNoCompileCmdsDepsDefined = -1;
    struct variable *pTarget    = kbuild_get_variable("target");
    struct variable *pSource    = kbuild_get_variable("source");
    struct variable *pDefPath   = kbuild_get_variable("defpath");
    struct variable *pType      = kbuild_get_variable("type");
    struct variable *pBldType   = kbuild_get_variable("bld_type");
    struct variable *pBldTrg    = kbuild_get_variable("bld_trg");
    struct variable *pBldTrgArch= kbuild_get_variable("bld_trg_arch");
    struct variable *pBldTrgCpu = kbuild_get_variable("bld_trg_cpu");
    struct variable *pTool      = kbuild_get_source_tool(pTarget, pSource, pType, pBldTrg, pBldTrgArch, "tool");
    struct variable *pOutBase   = kbuild_get_object_base(pTarget, pSource, "outbase");
    struct variable *pObjSuff   = kbuild_get_object_suffix(pTarget, pSource, pTool, pType, pBldTrg, pBldTrgArch, "objsuff");
    struct variable *pDefs, *pIncs, *pFlags, *pDeps, *pOrderDeps, *pDirDep, *pDep, *pVar, *pOutput;
    struct variable *pObj       = kbuild_set_object_name_and_dep_and_dirdep_and_PATH_target_source(pTarget, pSource, pOutBase, pObjSuff, "obj", &pDep, &pDirDep);
    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,
                                            "DEFS", "defs", 1/* left-to-right */);
    pIncs      = kbuild_collect_source_prop(pTarget, pSource, pTool, &Sdks, pType, pBldType, pBldTrg, pBldTrgArch, pBldTrgCpu, pDefPath,
                                            "INCS", "incs", -1/* right-to-left */);
    pFlags     = kbuild_collect_source_prop(pTarget, pSource, pTool, &Sdks, pType, pBldType, pBldTrg, pBldTrgArch, pBldTrgCpu, NULL,
                                            "FLAGS", "flags", 1/* left-to-right */);
    pDeps      = kbuild_collect_source_prop(pTarget, pSource, pTool, &Sdks, pType, pBldType, pBldTrg, pBldTrgArch, pBldTrgCpu, pDefPath,
                                            "DEPS", "deps", 1/* left-to-right */);
    pOrderDeps = kbuild_collect_source_prop(pTarget, pSource, pTool, &Sdks, pType, pBldType, pBldTrg, pBldTrgArch, pBldTrgCpu, pDefPath,
                                            "ORDERDEPS", "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)
        kbuild_apply_defpath(pDefPath, &pSource->value, &pSource->value_length, &pSource->value_alloc_len, 1 /* can free */);

    /*
    # dependencies
    ifndef NO_COMPILE_CMDS_DEPS
    _DEPFILES_INCLUDED += $(dep)
    $(if $(wildcard $(dep)),$(eval include $(dep)))
    endif
     */
    if (s_fNoCompileCmdsDepsDefined == -1)
        s_fNoCompileCmdsDepsDefined = kbuild_lookup_variable("NO_COMPILE_CMDS_DEPS") != NULL;
    if (!s_fNoCompileCmdsDepsDefined)
    {
        do_variable_definition(NILF, "_DEPFILES_INCLUDED", pDep->value, o_file, f_append, 0 /* !target_var */);
        eval_include_dep(pDep->value, NILF);
    }

    /*
    # 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)
    */
    cch = sizeof("TOOL_") + pTool->value_length + sizeof("_COMPILE_") + pType->value_length + sizeof("_OUTPUT_MAYBE");
    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);
    do_variable_definition(NILF, pszDstVar, pVar->value, o_file, f_simple, 0 /* !target_var */);

    memcpy(pszSrc, "_OUTPUT", sizeof("_OUTPUT"));
    memcpy(pszDst, "_OUTPUT_", sizeof("_OUTPUT_"));
    pVar = kbuild_get_recursive_variable(pszSrcVar);
    pOutput = do_variable_definition(NILF, pszDstVar, pVar->value, o_file, 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);
    pOutput = do_variable_definition(NILF, pszDstVar, pVar ? pVar->value : "", o_file, 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);
    do_variable_definition(NILF, pszDstVar, pszVal, o_file, f_simple, 0 /* !target_var */);
    free(pszVal);

    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);
    do_variable_definition(NILF, pszDstVar, pszVal, o_file, f_simple, 0 /* !target_var */);
    free(pszVal);

    /*
    _OUT_FILES      += $($(target)_$(source)_OUTPUT_)
    */
    pVar = kbuild_get_variable("_OUT_FILES");
    psz = pszVal = xmalloc(pVar->value_length + 1 + pOutput->value_length + 1);
    memcpy(psz, pVar->value, pVar->value_length); psz += pVar->value_length;
    *psz++ = ' ';
    memcpy(psz, pOutput->value, pOutput->value_length + 1);
    do_variable_definition(NILF, "_OUT_FILES", pszVal, o_file, f_simple, 0 /* !target_var */);
    free(pszVal);

    /*
    $(target)_OBJS_ += $(obj)
    */
    memcpy(pszDstVar + pTarget->value_length, "_OBJS_", sizeof("_OBJS_"));
    do_variable_definition(NILF, pszDstVar, pObj->value, o_file, f_append, 0 /* !target_var */);

    /*
    $(eval $(def_target_source_rule))
    */
    pVar = kbuild_get_recursive_variable("def_target_source_rule");
    pszVal = allocated_variable_expand(pVar->value);

    install_variable_buffer(&pszSavedVarBuf, &cchSavedVarBuf);
    eval_buffer(pszVal);
    restore_variable_buffer(pszSavedVarBuf, cchSavedVarBuf);

    free(pszVal);

    kbuild_put_sdks(&Sdks);
    return variable_buffer_output(o, "", 1);
}


#endif /* KMK_HELPERS */


Generated by  Doxygen 1.6.0   Back to index