VirtualBox

Changeset 98319 in vbox


Ignore:
Timestamp:
Jan 26, 2023 3:31:55 PM (2 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
155538
Message:

scm: Started on implement a more thoughout cleanup of kmk makefiles. bugref:10348

Location:
trunk/src/bldprogs
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/bldprogs/scm.cpp

    r98113 r98319  
    105105    SCMOPT_NO_RC_USE,
    106106    SCMOPT_UNRESTRICTED_RC_USE,
     107    SCMOPT_STANDARIZE_KMK,
     108    SCMOPT_NO_STANDARIZE_KMK,
    107109    SCMOPT_UPDATE_COPYRIGHT_YEAR,
    108110    SCMOPT_NO_UPDATE_COPYRIGHT_YEAR,
     
    215217    /* .fNoASMMemPageUse = */                       false,
    216218    /* .fOnlyHrcVrcInsteadOfRc */                   false,
     219    /* .fStandarizeKmk */                           false,
    217220    /* .fUpdateCopyrightYear = */                   false,
    218221    /* .fExternalCopyright = */                     false,
     
    274277    { "--no-rc-use",                        SCMOPT_NO_RC_USE,                       RTGETOPT_REQ_NOTHING },
    275278    { "--unrestricted-rc-use",              SCMOPT_UNRESTRICTED_RC_USE,             RTGETOPT_REQ_NOTHING },
     279    { "--standarize-kmk",                   SCMOPT_STANDARIZE_KMK,                  RTGETOPT_REQ_NOTHING },
     280    { "--no-standarize-kmk",                SCMOPT_NO_STANDARIZE_KMK,               RTGETOPT_REQ_NOTHING },
    276281    { "--update-copyright-year",            SCMOPT_UPDATE_COPYRIGHT_YEAR,           RTGETOPT_REQ_NOTHING },
    277282    { "--no-update-copyright-year",         SCMOPT_NO_UPDATE_COPYRIGHT_YEAR,        RTGETOPT_REQ_NOTHING },
     
    12261231            return VINF_SUCCESS;
    12271232
     1233        case SCMOPT_STANDARIZE_KMK:
     1234            pSettings->fStandarizeKmk = true;
     1235            return VINF_SUCCESS;
     1236        case SCMOPT_NO_STANDARIZE_KMK:
     1237            pSettings->fStandarizeKmk = false;
     1238            return VINF_SUCCESS;
     1239
    12281240        case SCMOPT_UPDATE_COPYRIGHT_YEAR:
    12291241            pSettings->fUpdateCopyrightYear = true;
     
    22222234bool ScmFixManually(PSCMRWSTATE pState, const char *pszFormat, ...)
    22232235{
     2236    va_list va;
     2237    va_start(va, pszFormat);
     2238    ScmFixManuallyV(pState, pszFormat, va);
     2239    va_end(va);
     2240    return false;
     2241}
     2242
     2243
     2244/**
     2245 * Prints message indicating that something requires manual fixing.
     2246 *
     2247 * @returns false
     2248 * @param   pState              The rewrite state.  Optional.
     2249 * @param   rc                  The error code.
     2250 * @param   pszFormat           The message format string.
     2251 * @param   va                  Format arguments.
     2252 */
     2253bool ScmFixManuallyV(PSCMRWSTATE pState, const char *pszFormat, va_list va)
     2254{
    22242255    pState->fNeedsManualRepair = true;
    22252256
     
    22292260        pState->fFirst = true;
    22302261    }
    2231     va_list va;
    2232     va_start(va, pszFormat);
    2233     RTPrintf("%s: error/fixme: %s: %N", g_szProgName, pState->pszFilename, pszFormat, &va);
    2234     va_end(va);
     2262    va_list vaCopy;
     2263    va_copy(vaCopy, va);
     2264    RTPrintf("%s: error/fixme: %s: %N", g_szProgName, pState->pszFilename, pszFormat, &vaCopy);
     2265    va_end(vaCopy);
    22352266
    22362267    return false;
     
    29612992                         g_Defaults.fOnlyHrcVrcInsteadOfRc);
    29622993                break;
     2994            case SCMOPT_STANDARIZE_KMK:
     2995                RTPrintf("      Clean up kmk files (the makefile-kmk action).  Default: %RTbool\n", g_Defaults.fStandarizeKmk);
     2996                break;
    29632997            case SCMOPT_UPDATE_COPYRIGHT_YEAR:
    29642998                RTPrintf("      Update the copyright year.  Default: %RTbool\n", g_Defaults.fUpdateCopyrightYear);
  • trunk/src/bldprogs/scm.h

    r98103 r98319  
    377377    /** No rc declarations allowed, only hrc or vrc depending on the result type. */
    378378    bool            fOnlyHrcVrcInsteadOfRc;
     379
     380    /** Whether to standarize kmk makefiles. */
     381    bool            fStandarizeKmk;
    379382
    380383    /** Update the copyright year. */
     
    479482bool ScmError(PSCMRWSTATE pState, int rc, const char *pszFormat, ...) RT_IPRT_FORMAT_ATTR(3, 4);
    480483bool ScmFixManually(PSCMRWSTATE pState, const char *pszFormat, ...) RT_IPRT_FORMAT_ATTR(2, 3);
     484bool ScmFixManuallyV(PSCMRWSTATE pState, const char *pszFormat, va_list va) RT_IPRT_FORMAT_ATTR(2, 0);
    481485
    482486extern const char g_szTabSpaces[16+1];
  • trunk/src/bldprogs/scmrw.cpp

    r98103 r98319  
    12111211    return false;
    12121212}
     1213
     1214
     1215
     1216/*********************************************************************************************************************************
     1217*   Copyright & License                                                                                                          *
     1218*********************************************************************************************************************************/
    12131219
    12141220/**
     
    21542160
    21552161
     2162
     2163/*********************************************************************************************************************************
     2164*   kBuild Makefiles                                                                                                             *
     2165*********************************************************************************************************************************/
     2166
    21562167/**
    21572168 * Makefile.kup are empty files, enforce this.
     
    21732184}
    21742185
     2186typedef enum KMKTOKEN
     2187{
     2188    kKmkToken_Word = 0,
     2189
     2190    /* Conditionals: */
     2191    kKmkToken_ifeq,
     2192    kKmkToken_ifneq,
     2193    kKmkToken_if1of,
     2194    kKmkToken_ifn1of,
     2195    kKmkToken_ifdef,
     2196    kKmkToken_ifndef,
     2197    kKmkToken_if,
     2198    kKmkToken_else,
     2199    kKmkToken_endif,
     2200
     2201    /* Includes: */
     2202    kKmkToken_include,
     2203    kKmkToken_sinclude,
     2204    kKmkToken_dash_include,
     2205    kKmkToken_includedep,
     2206    kKmkToken_includedep_queue,
     2207    kKmkToken_includedep_flush,
     2208
     2209    /* Others: */
     2210    kKmkToken_define,
     2211    kKmkToken_endef,
     2212    kKmkToken_export,
     2213    kKmkToken_unexport,
     2214    kKmkToken_local,
     2215    kKmkToken_override
     2216} KMKTOKEN;
     2217
     2218typedef struct KMKPARSER
     2219{
     2220    struct
     2221    {
     2222        KMKTOKEN        enmToken;
     2223        uint32_t        iLine;
     2224        bool            fIgnoreNesting;
     2225    } aDepth[64];
     2226    unsigned            iDepth;
     2227    unsigned            iActualDepth;
     2228    bool                fInReceipt;
     2229
     2230    /** The current line number (for error messages and peeking).   */
     2231    uint32_t            iLine;
     2232    /** The EOL type of the current line. */
     2233    SCMEOL              enmEol;
     2234    /** The length of the current line. */
     2235    size_t              cchLine;
     2236    /** Pointer to the start of the current line. */
     2237    char const         *pchLine;
     2238
     2239    /** The SCM rewriter state. */
     2240    PSCMRWSTATE         pState;
     2241    /** The input stream. */
     2242    PSCMSTREAM          pIn;
     2243    /** The output stream. */
     2244    PSCMSTREAM          pOut;
     2245    /** The settings. */
     2246    PCSCMSETTINGSBASE   pSettings;
     2247    /** Scratch buffer. */
     2248    char                szBuf[4096];
     2249} KMKPARSER;
     2250
     2251static KMKTOKEN scmKmkIdentifyToken(const char *pchWord, size_t cchWord)
     2252{
     2253    static struct { const char *psz; uint32_t cch; KMKTOKEN enmToken; } s_aTokens[] =
     2254    {
     2255        { RT_STR_TUPLE("if"),               kKmkToken_if },
     2256        { RT_STR_TUPLE("ifeq"),             kKmkToken_ifeq },
     2257        { RT_STR_TUPLE("ifneq"),            kKmkToken_ifneq },
     2258        { RT_STR_TUPLE("if1of"),            kKmkToken_if1of },
     2259        { RT_STR_TUPLE("ifn1of"),           kKmkToken_ifn1of },
     2260        { RT_STR_TUPLE("ifdef"),            kKmkToken_ifdef },
     2261        { RT_STR_TUPLE("ifndef"),           kKmkToken_ifndef },
     2262        { RT_STR_TUPLE("else"),             kKmkToken_else },
     2263        { RT_STR_TUPLE("endif"),            kKmkToken_endif },
     2264        { RT_STR_TUPLE("include"),          kKmkToken_include },
     2265        { RT_STR_TUPLE("sinclude"),         kKmkToken_sinclude },
     2266        { RT_STR_TUPLE("-include"),         kKmkToken_dash_include },
     2267        { RT_STR_TUPLE("includedep"),       kKmkToken_includedep },
     2268        { RT_STR_TUPLE("includedep-queue"), kKmkToken_includedep_queue },
     2269        { RT_STR_TUPLE("includedep-flush"), kKmkToken_includedep_flush },
     2270        { RT_STR_TUPLE("define"),           kKmkToken_define },
     2271        { RT_STR_TUPLE("endef"),            kKmkToken_endef },
     2272        { RT_STR_TUPLE("export"),           kKmkToken_export },
     2273        { RT_STR_TUPLE("unexport"),         kKmkToken_unexport },
     2274        { RT_STR_TUPLE("local"),            kKmkToken_local },
     2275        { RT_STR_TUPLE("override"),         kKmkToken_override },
     2276    };
     2277    char chFirst = *pchWord;
     2278    if (   chFirst == 'i'
     2279        || chFirst == 'e'
     2280        || chFirst == 'd'
     2281        || chFirst == 's'
     2282        || chFirst == '-'
     2283        || chFirst == 'u'
     2284        || chFirst == 'l'
     2285        || chFirst == 'o')
     2286    {
     2287        for (size_t i = 0; i < RT_ELEMENTS(s_aTokens); i++)
     2288            if (   s_aTokens[i].cch == cchWord
     2289                && *s_aTokens[i].psz == chFirst
     2290                && memcmp(s_aTokens[i].psz, pchWord, cchWord) == 0)
     2291                return s_aTokens[i].enmToken;
     2292    }
     2293#ifdef VBOX_STRICT
     2294    else
     2295        for (size_t i = 0; i < RT_ELEMENTS(s_aTokens); i++)
     2296            Assert(chFirst != *s_aTokens[i].psz);
     2297#endif
     2298
     2299    return kKmkToken_Word;
     2300}
     2301
     2302
     2303/**
     2304 * Gives up on the current line, copying it as it and requesting manual repair.
     2305 */
     2306static bool scmKmkGiveUp(KMKPARSER *pParser, const char *pszFormat, ...)
     2307{
     2308    va_list va;
     2309    va_start(va, pszFormat);
     2310    ScmFixManually(pParser->pState, "%u: %N\n", pParser->iLine, pszFormat, &va);
     2311    va_end(va);
     2312
     2313    ScmStreamPutLine(pParser->pOut, pParser->pchLine, pParser->cchLine, pParser->enmEol);
     2314    return false;
     2315}
     2316
     2317
     2318static bool scmKmkIsLineWithContinuationSlow(const char *pchLine, size_t cchLine)
     2319{
     2320    size_t cchSlashes = 1;
     2321    cchLine--;
     2322    while (cchSlashes < cchLine && pchLine[cchLine - cchSlashes - 1] == '\\')
     2323        cchSlashes++;
     2324    return RT_BOOL(cchSlashes & 1);
     2325}
     2326
     2327
     2328DECLINLINE(bool) scmKmkIsLineWithContinuation(const char *pchLine, size_t cchLine)
     2329{
     2330    if (cchLine == 0 || pchLine[cchLine - 1] != '\\')
     2331        return false;
     2332    return scmKmkIsLineWithContinuationSlow(pchLine, cchLine);
     2333}
     2334
     2335
     2336/**
     2337 * Finds the length of a line where line continuation is in play.
     2338 *
     2339 * @returns Length from start of current line to the final unescaped EOL.
     2340 * @param   pParser         The KMK parser state.
     2341 * @param   pcLine          Where to return the number of lines.  Optional.
     2342 * @param   pcchMaxLeadWord Where to return the max lead word length on
     2343 *                          subsequent lines. Used to help balance multi-line
     2344 *                          'if' statements (imperfect).  Optional.
     2345 */
     2346static size_t scmKmkLineContinuationPeek(KMKPARSER *pParser, uint32_t *pcLines, size_t *pcchMaxLeadWord)
     2347{
     2348    size_t const offSaved       = ScmStreamTell(pParser->pIn);
     2349    uint32_t     cLines         = 1;
     2350    size_t       cchMaxLeadWord = 0;
     2351    const char  *pchLine        = pParser->pchLine;
     2352    size_t       cchLine        = pParser->cchLine;
     2353    SCMEOL       enmEol;
     2354    for (;;)
     2355    {
     2356        /* Return if no line continuation (or end of stream): */
     2357        if (   cchLine == 0
     2358            || !scmKmkIsLineWithContinuation(pchLine, cchLine)
     2359            || ScmStreamIsEndOfStream(pParser->pIn))
     2360        {
     2361            ScmStreamSeekAbsolute(pParser->pIn, offSaved);
     2362            if (pcLines)
     2363                *pcLines = cLines;
     2364            if (pcchMaxLeadWord)
     2365                *pcchMaxLeadWord = cchMaxLeadWord;
     2366            return (size_t)(pchLine - pParser->pchLine) + cchLine;
     2367        }
     2368
     2369        /* Get the next line: */
     2370        pchLine = ScmStreamGetLine(pParser->pIn, &cchLine, &enmEol);
     2371        cLines++;
     2372
     2373        /* Check the length of the first word if requested: */
     2374        if (pcchMaxLeadWord)
     2375        {
     2376            size_t offLine = 0;
     2377            while (offLine < cchLine && RT_C_IS_BLANK(pchLine[offLine]))
     2378                offLine++;
     2379
     2380            size_t const offStartWord = offLine;
     2381            while (offLine < cchLine && !RT_C_IS_BLANK(pchLine[offLine]))
     2382                offLine++;
     2383
     2384            if (offLine - offStartWord > cchMaxLeadWord)
     2385                cchMaxLeadWord = offLine - offStartWord;
     2386        }
     2387    }
     2388}
     2389
     2390
     2391static bool scmKmkPushNesting(KMKPARSER *pParser, KMKTOKEN enmToken)
     2392{
     2393    uint32_t iDepth = pParser->iDepth;
     2394    if (iDepth + 1 >= RT_ELEMENTS(pParser->aDepth))
     2395        return ScmError(pParser->pState, VERR_ASN1_TOO_DEEPLY_NESTED /*?*/, "%u: Too deep if/define nesting!\n", pParser->iLine);
     2396
     2397    pParser->aDepth[iDepth].enmToken       = enmToken;
     2398    pParser->aDepth[iDepth].iLine          = pParser->iLine;
     2399    pParser->aDepth[iDepth].fIgnoreNesting = false;
     2400    pParser->iDepth        = iDepth + 1;
     2401    pParser->iActualDepth += 1;
     2402    return true;
     2403}
     2404
     2405
     2406/**
     2407 * Skips a string stopping at @a chStop1 or @a chStop2, taking $() and ${} into
     2408 * account.
     2409 */
     2410static size_t scmKmkSkipExpString(const char *pchLine, size_t cchLine, size_t off, char chStop1, char chStop2 = '\0')
     2411{
     2412    unsigned iExpDepth = 0;
     2413    char     ch;
     2414    while (   off < cchLine
     2415           && (ch = pchLine[off])
     2416           && (    (ch != chStop1 && ch != chStop2)
     2417                || iExpDepth > 0))
     2418    {
     2419        off++;
     2420        if (ch == '$')
     2421        {
     2422            ch = pchLine[off];
     2423            if (ch == '(' || ch == '{')
     2424            {
     2425                iExpDepth++;
     2426                off++;
     2427            }
     2428        }
     2429        else if ((ch == ')' || ch == '}') && iExpDepth > 0)
     2430            iExpDepth--;
     2431    }
     2432    return off;
     2433}
     2434
     2435
     2436static bool scmKmkTailComment(KMKPARSER *pParser, const char *pchLine, size_t cchLine, size_t offSrc, char **ppszDst)
     2437{
     2438    size_t const offSrcStart = offSrc;
     2439
     2440    /* Skip blanks. */
     2441    while (offSrc < cchLine && RT_C_IS_SPACE(pchLine[offSrc]))
     2442        offSrc++;
     2443    if (offSrc >= cchLine)
     2444        return true;
     2445
     2446    /* Is it a comment? */
     2447    char *pszDst = *ppszDst;
     2448    if (pchLine[offSrc] == '#')
     2449    {
     2450        /* Try preserve the start column number. */
     2451        size_t const offDst = pszDst - pParser->szBuf;
     2452        if (offDst < offSrc)
     2453        {
     2454            memset(pszDst, ' ', offSrc - offDst);
     2455            pszDst += offSrc - offDst;
     2456        }
     2457        else if (offSrc != offSrcStart)
     2458            *pszDst++ = ' ';
     2459
     2460        *ppszDst = pszDst = (char *)mempcpy(pszDst, &pchLine[offSrc], cchLine - offSrc);
     2461        return cchLine - offSrcStart != (size_t)(pszDst - &pParser->szBuf[offDst])
     2462            || memcmp(&pParser->szBuf[offDst], &pchLine[offSrcStart], cchLine - offSrcStart) != 0;
     2463    }
     2464
     2465    /* Complain and copy out the text unmodified. */
     2466    ScmError(pParser->pState, VERR_PARSE_ERROR, "%u:%u: Expected comment, found: %.*s",
     2467             pParser->iLine, offSrc, cchLine - offSrc, &pchLine[offSrc]);
     2468    *ppszDst = (char *)mempcpy(pszDst, &pchLine[offSrcStart], cchLine - offSrcStart);
     2469    return false;
     2470}
     2471
     2472
     2473/**
     2474 * Deals with: ifeq, ifneq, if1of and ifn1of
     2475 */
     2476static bool scmKmkHandleIfParentheses(KMKPARSER *pParser, size_t offToken, KMKTOKEN enmToken, size_t cchToken, bool fElse)
     2477{
     2478    const char * const pchLine   = pParser->pchLine;
     2479    size_t  const      cchLine   = pParser->cchLine;
     2480    uint32_t const     cchIndent = pParser->iActualDepth
     2481                                 - (fElse && pParser->iDepth > 0 && !pParser->aDepth[pParser->iDepth].fIgnoreNesting);
     2482
     2483    /*
     2484     * Push it onto the stack.  All these nestings are relevant.
     2485     */
     2486    if (!scmKmkPushNesting(pParser, enmToken))
     2487        return false;
     2488
     2489    /*
     2490     * We do not allow line continuation for these.
     2491     */
     2492    if (scmKmkIsLineWithContinuation(pchLine, cchLine))
     2493        return scmKmkGiveUp(pParser, "Line continuation not allowed with '%.*s' directive.", cchToken, &pchLine[offToken]);
     2494
     2495    /*
     2496     * We stage the modified line in the buffer, so check that the line isn't
     2497     * too long (it seriously should be).
     2498     */
     2499    if (cchLine + cchIndent + 32 > sizeof(pParser->szBuf))
     2500        return scmKmkGiveUp(pParser, "Line too long for a '%.*s' directive: %u chars", cchToken, &pchLine[offToken], cchLine);
     2501    char *pszDst = pParser->szBuf;
     2502
     2503    /*
     2504     * Emit indent and initial token.
     2505     */
     2506    memset(pszDst, ' ', cchIndent);
     2507    pszDst += cchIndent;
     2508
     2509    if (fElse)
     2510        pszDst = (char *)mempcpy(pszDst, RT_STR_TUPLE("else "));
     2511
     2512    memcpy(pszDst, &pchLine[offToken], cchToken);
     2513    pszDst += cchToken;
     2514
     2515    size_t offSrc    = offToken + cchToken;
     2516    bool   fModified = offSrc != (size_t)(pszDst - &pParser->szBuf[0])
     2517                    || memcmp(pchLine, pszDst, offSrc) != 0;
     2518
     2519    /*
     2520     * There shall be exactly one space between the token and the opening parenthesis.
     2521     */
     2522    if (pchLine[offSrc] == ' ' && pchLine[offSrc + 1] == '(')
     2523        offSrc += 2;
     2524    else
     2525    {
     2526        fModified = true;
     2527        while (offSrc < cchLine && RT_C_IS_BLANK(pchLine[offSrc]))
     2528            offSrc++;
     2529        if (pchLine[offSrc] != '(')
     2530            return scmKmkGiveUp(pParser, "Expected '(' to follow '%.*s'", cchToken, &pchLine[offToken]);
     2531        offSrc++;
     2532    }
     2533    *pszDst++ = ' ';
     2534    *pszDst++ = '(';
     2535
     2536    /*
     2537     * There shall be no blanks after the opening parenthesis.
     2538     */
     2539    if (RT_C_IS_SPACE(pchLine[offSrc]))
     2540    {
     2541        fModified = true;
     2542        while (offSrc < cchLine && RT_C_IS_BLANK(pchLine[offSrc]))
     2543            offSrc++;
     2544    }
     2545
     2546    /*
     2547     * Work up to the ',' separator.  It shall likewise not be preceeded by any spaces.
     2548     * Need to take $(func 1,2,3) calls into account here, so we trac () and {} while
     2549     * skipping ahead.
     2550     */
     2551    if (pchLine[offSrc] != ',')
     2552    {
     2553        size_t const offSrcStart = offSrc;
     2554        offSrc = scmKmkSkipExpString(pchLine, cchLine, offSrc, ',');
     2555        if (pchLine[offSrc] != ',')
     2556            return scmKmkGiveUp(pParser, "Expected ',' somewhere after '%.*s('", cchToken, &pchLine[offToken]);
     2557
     2558        size_t cchCopy = offSrc - offSrcStart;
     2559        while (cchCopy > 0 && RT_C_IS_BLANK(pchLine[offSrcStart + cchCopy - 1]))
     2560        {
     2561            fModified = true;
     2562            cchCopy--;
     2563        }
     2564
     2565        pszDst = (char *)mempcpy(pszDst, &pchLine[offSrcStart], cchCopy);
     2566    }
     2567    /* 'if1of(, stuff)' does not make sense in committed code: */
     2568    else if (enmToken == kKmkToken_if1of || enmToken == kKmkToken_ifn1of)
     2569        return scmKmkGiveUp(pParser, "Left set cannot be empty for '%.*s'", cchToken, &pchLine[offToken]);
     2570    offSrc++;
     2571    *pszDst++ = ',';
     2572
     2573    /*
     2574     * For if1of and ifn1of we require a space after the comma, whereas ifeq and
     2575     * ifneq shall not have any blanks.  This is to help tell them apart.
     2576     */
     2577    if (enmToken == kKmkToken_if1of || enmToken == kKmkToken_ifn1of)
     2578    {
     2579        *pszDst++ = ' ';
     2580        if (pchLine[offSrc] == ' ')
     2581            offSrc++;
     2582    }
     2583    while (offSrc < cchLine && RT_C_IS_BLANK(pchLine[offSrc]))
     2584    {
     2585        fModified = true;
     2586        offSrc++;
     2587    }
     2588
     2589    if (pchLine[offSrc] != ')')
     2590    {
     2591        size_t const offSrcStart = offSrc;
     2592        offSrc = scmKmkSkipExpString(pchLine, cchLine, offSrc, ')');
     2593        if (pchLine[offSrc] != ')')
     2594            return scmKmkGiveUp(pParser, "No closing parenthesis for '%.*s'?", cchToken, &pchLine[offToken]);
     2595
     2596        size_t cchCopy = offSrc - offSrcStart;
     2597        while (cchCopy > 0 && RT_C_IS_BLANK(pchLine[offSrcStart + cchCopy - 1]))
     2598        {
     2599            fModified = true;
     2600            cchCopy--;
     2601        }
     2602
     2603        pszDst = (char *)mempcpy(pszDst, &pchLine[offSrcStart], cchCopy);
     2604    }
     2605    /* 'if1of(stuff, )' does not make sense in committed code: */
     2606    else if (enmToken == kKmkToken_if1of || enmToken == kKmkToken_ifn1of)
     2607        return scmKmkGiveUp(pParser, "Right set cannot be empty for '%.*s'", cchToken, &pchLine[offToken]);
     2608    offSrc++;
     2609    *pszDst++ = ')';
     2610
     2611    /*
     2612     * Handle comment.
     2613     */
     2614    if (offSrc < cchLine)
     2615        fModified |= scmKmkTailComment(pParser, pchLine, cchLine, offSrc, &pszDst);
     2616
     2617    /*
     2618     * Done.
     2619     */
     2620    *pszDst = '\0';
     2621    ScmStreamPutLine(pParser->pOut, pParser->szBuf, pszDst - pParser->szBuf, pParser->enmEol);
     2622    return fModified;
     2623}
     2624
     2625
     2626/**
     2627 * Deals with: if, ifdef and ifndef
     2628 */
     2629static bool scmKmkHandleIfSpace(KMKPARSER *pParser, size_t offToken, KMKTOKEN enmToken, size_t cchToken, bool fElse)
     2630{
     2631    const char     *pchLine   = pParser->pchLine;
     2632    size_t          cchLine   = pParser->cchLine;
     2633    uint32_t const  cchIndent = pParser->iActualDepth
     2634                              - (fElse && pParser->iDepth > 0 && !pParser->aDepth[pParser->iDepth].fIgnoreNesting);
     2635
     2636    /*
     2637     * Push it onto the stack.
     2638     *
     2639     * For ifndef we ignore the outmost ifndef in non-Makefile.kmk files, if
     2640     * the define matches the typical pattern for a file blocker.
     2641     */
     2642    if (!fElse)
     2643    {
     2644        if (!scmKmkPushNesting(pParser, enmToken))
     2645            return false;
     2646    }
     2647    else
     2648    {
     2649        pParser->aDepth[pParser->iDepth - 1].enmToken = enmToken;
     2650        pParser->aDepth[pParser->iDepth - 1].iLine    = pParser->iLine;
     2651    }
     2652    bool fIgnoredNesting = false;
     2653    if (enmToken == kKmkToken_ifndef)
     2654    {
     2655        /** @todo */
     2656    }
     2657
     2658    /*
     2659     * We do not allow line continuation for these.
     2660     */
     2661    uint32_t cLines         = 1;
     2662    size_t   cchMaxLeadWord = 0;
     2663    size_t   cchLineTotal   = cchLine;
     2664    if (scmKmkIsLineWithContinuation(pchLine, cchLine))
     2665    {
     2666        if (enmToken != kKmkToken_if)
     2667            return scmKmkGiveUp(pParser, "Line continuation not allowed with '%.*s' directive.", cchToken, &pchLine[offToken]);
     2668        cchLineTotal = scmKmkLineContinuationPeek(pParser, &cLines, &cchMaxLeadWord);
     2669    }
     2670
     2671    /*
     2672     * We stage the modified line in the buffer, so check that the line isn't
     2673     * too long (plain if can be long, but not ifndef/ifdef).
     2674     */
     2675    if (cchLineTotal + pParser->iActualDepth + 32 > sizeof(pParser->szBuf))
     2676        return scmKmkGiveUp(pParser, "Line too long for a '%.*s' directive: %u chars",
     2677                            cchToken, &pchLine[offToken], cchLineTotal);
     2678    char *pszDst = pParser->szBuf;
     2679
     2680    /*
     2681     * Emit indent and initial token.
     2682     */
     2683    memset(pszDst, ' ', cchIndent);
     2684    pszDst += cchIndent;
     2685
     2686    if (fElse)
     2687        pszDst = (char *)mempcpy(pszDst, RT_STR_TUPLE("else "));
     2688
     2689    memcpy(pszDst, &pchLine[offToken], cchToken);
     2690    pszDst += cchToken;
     2691
     2692    size_t offSrc    = offToken + cchToken;
     2693    bool   fModified = offSrc != (size_t)(pszDst - &pParser->szBuf[0])
     2694                    || memcmp(pchLine, pszDst, offSrc) != 0;
     2695
     2696    /*
     2697     * ifndef/ifdef shall have exactly one space.  For 'if' we allow up to 4, but
     2698     * we'll deal with that further down.
     2699     */
     2700    size_t cchSpaces = 0;
     2701    while (offSrc < cchLine && RT_C_IS_BLANK(pchLine[offSrc]))
     2702    {
     2703        fModified |= pchLine[offSrc] != ' ';
     2704        cchSpaces++;
     2705        offSrc++;
     2706    }
     2707    if (cchSpaces == 0)
     2708        return scmKmkGiveUp(pParser, "Nothing following '%.*s' or bogus line continuation?", cchToken, &pchLine[offToken]);
     2709    *pszDst++ = ' ';
     2710
     2711    /*
     2712     * For ifdef and ifndef there now comes a single word.
     2713     */
     2714    if (enmToken != kKmkToken_if)
     2715    {
     2716        fModified |= cchSpaces != 1;
     2717
     2718        size_t const offSrcStart = offSrc;
     2719        offSrc = scmKmkSkipExpString(pchLine, cchLine, offSrc, ' ', '\t'); /** @todo probably not entirely correct */
     2720        if (offSrc == offSrcStart)
     2721            return scmKmkGiveUp(pParser, "No word following '%.*s'?", cchToken, &pchLine[offToken]);
     2722
     2723        pszDst = (char *)mempcpy(pszDst, &pchLine[offSrcStart], offSrc - offSrcStart);
     2724    }
     2725    /*
     2726     * While for 'if' things are more complicated, especially if it spans more
     2727     * than one line.
     2728     */
     2729    else if (cLines <= 1)
     2730    {
     2731        /* Single line expression: Just assume the expression goes up to the
     2732           EOL or comment hash. Strip and copy as-is for now. */
     2733        fModified |= cchSpaces != 1;
     2734
     2735        const char *pchSrcHash = (const char *)memchr(&pchLine[offSrc], '#', cchLine - offSrc);
     2736        size_t      cchExpr    = pchSrcHash ? pchSrcHash - &pchLine[offSrc] : cchLine - offSrc;
     2737        while (cchExpr > 0 && RT_C_IS_BLANK(pchLine[offSrc + cchExpr - 1]))
     2738            cchExpr--;
     2739
     2740        pszDst = (char *)mempcpy(pszDst, &pchLine[offSrc], cchExpr);
     2741        offSrc += cchExpr;
     2742    }
     2743    else
     2744    {
     2745        /* Multi line expression: We normalize leading whitespace using
     2746           cchMaxLeadWord for now.  Expression on line 2+ are indented by two
     2747           extra characters, because we'd otherwise be puttin the operator on
     2748           the same level as the 'if', which would be confusing.  Thus:
     2749
     2750                if  expr1
     2751                  + expr2
     2752                endif
     2753
     2754                if   expr1
     2755                  || expr2
     2756                endif
     2757
     2758                if    expr3
     2759                  vtg expr4
     2760                endif
     2761
     2762           We do '#' / EOL handling for the final line the same way as above.
     2763
     2764           Later we should add the ability to rework the expression properly,
     2765           making sure new lines starts with operators and such. */
     2766        /** @todo Implement simples expression parser and indenter, possibly also
     2767         *        removing unnecessary parentheses.  Can be shared with C/C++. */
     2768        if (cchMaxLeadWord > 3)
     2769            return scmKmkGiveUp(pParser,
     2770                                "Bogus multi-line 'if' expression! Extra lines must start with operator (cchMaxLeadWord=%u).",
     2771                                cchMaxLeadWord);
     2772        fModified |= cchSpaces != cchMaxLeadWord + 1;
     2773        memset(pszDst, ' ', cchMaxLeadWord);
     2774        pszDst += cchMaxLeadWord;
     2775
     2776        size_t cchSrcContIndent = offToken + 2;
     2777        for (uint32_t iSubLine = 0; iSubLine < cLines - 1; iSubLine++)
     2778        {
     2779            /* Trim the line. */
     2780            size_t offSrcEnd = cchLine;
     2781            Assert(pchLine[offSrcEnd - 1] == '\\');
     2782            offSrcEnd--;
     2783
     2784            if (pchLine[offSrcEnd - 1] == '\\')
     2785                return scmKmkGiveUp(pParser, "Escaped '\\' before line continuation in 'if' expression is not allowed!");
     2786
     2787            while (offSrcEnd > offSrc && RT_C_IS_BLANK(pchLine[offSrcEnd - 1]))
     2788                offSrcEnd--;
     2789
     2790            /* Comments with line continuation is not allowed in commited makefiles. */
     2791            if (offSrc < offSrcEnd && memchr(&pchLine[offSrc], '#', cchLine - offSrc) != NULL)
     2792                return scmKmkGiveUp(pParser, "Comment in multi-line 'if' expression is not allowed to start before the final line!");
     2793
     2794            /* Output it. */
     2795            if (offSrc < offSrcEnd)
     2796            {
     2797                if (iSubLine > 0 && offSrc > cchSrcContIndent)
     2798                {
     2799                    memset(pszDst, ' ', offSrc - cchSrcContIndent);
     2800                    pszDst += offSrc - cchSrcContIndent;
     2801                }
     2802                pszDst = (char *)mempcpy(pszDst, &pchLine[offSrc], offSrcEnd - offSrc);
     2803                *pszDst++ = ' ';
     2804            }
     2805            else if (iSubLine == 0)
     2806                return scmKmkGiveUp(pParser, "Expected expression after 'if', not line continuation!");
     2807            *pszDst++ = '\\';
     2808            *pszDst   = '\0';
     2809            size_t cchDst = (size_t)(pszDst - pParser->szBuf);
     2810            fModified |= cchDst != cchLine
     2811                      || memcmp(pParser->szBuf, pchLine, cchLine) != 0;
     2812            ScmStreamPutLine(pParser->pOut, pParser->szBuf, cchDst, pParser->enmEol);
     2813
     2814            /*
     2815             * Fetch the next line and start processing it.
     2816             */
     2817            pParser->pchLine = pchLine = ScmStreamGetLine(pParser->pIn, &pParser->cchLine, &pParser->enmEol);
     2818            if (!pchLine)
     2819                return ScmError(pParser->pState, VERR_INTERNAL_ERROR_3, "ScmStreamGetLine unexpectedly returned NULL!");
     2820            cchLine = pParser->cchLine;
     2821            pParser->iLine++;
     2822
     2823            /* Skip leading whitespace and adjust the source continuation indent: */
     2824            offSrc = 0;
     2825            while (offSrc < cchLine && RT_C_IS_SPACE(pchLine[offSrc]))
     2826                offSrc++;
     2827            /** @todo tabs */
     2828
     2829            if (iSubLine == 0)
     2830                cchSrcContIndent = offSrc;
     2831
     2832            /* Initial indent: */
     2833            pszDst = pParser->szBuf;
     2834            memset(pszDst, ' ', cchIndent + 2);
     2835            pszDst += cchIndent + 2;
     2836        }
     2837
     2838        /* Output the expression on the final line. */
     2839        const char *pchSrcHash = (const char *)memchr(&pchLine[offSrc], '#', cchLine - offSrc);
     2840        size_t      cchExpr    = pchSrcHash ? pchSrcHash - &pchLine[offSrc] : cchLine - offSrc;
     2841        while (cchExpr > 0 && RT_C_IS_BLANK(pchLine[offSrc + cchExpr - 1]))
     2842            cchExpr--;
     2843
     2844        pszDst = (char *)mempcpy(pszDst, &pchLine[offSrc], cchExpr);
     2845        offSrc += cchExpr;
     2846    }
     2847
     2848
     2849    /*
     2850     * Handle comment.
     2851     *
     2852     * Here we check for the "scm:ignore-nesting" directive that makes us not
     2853     * add indentation for this directive.  We do this on the destination buffer
     2854     * as that can be zero terminated and is therefore usable with strstr.
     2855     */
     2856    if (offSrc >= cchLine)
     2857        *pszDst = '\0';
     2858    else
     2859    {
     2860        char * const pszDstSrc = pszDst;
     2861        fModified |= scmKmkTailComment(pParser, pchLine, cchLine, offSrc, &pszDst);
     2862        *pszDst = '\0';
     2863
     2864        /* Check for special comment making us ignore the nesting. We do this in the
     2865
     2866           */
     2867        if (!fIgnoredNesting && strstr(pszDstSrc, "scm:ignore-nesting") != NULL)
     2868        {
     2869            pParser->aDepth[pParser->iDepth - 1].fIgnoreNesting = true;
     2870            pParser->iActualDepth--;
     2871        }
     2872    }
     2873
     2874    /*
     2875     * Done.
     2876     */
     2877    ScmStreamPutLine(pParser->pOut, pParser->szBuf, pszDst - pParser->szBuf, pParser->enmEol);
     2878    return fModified;
     2879}
     2880
     2881
     2882/**
     2883 * Deals with: else
     2884 */
     2885static bool scmKmkHandleElse(KMKPARSER *pParser, size_t offToken)
     2886{
     2887    const char * const pchLine   = pParser->pchLine;
     2888    size_t  const      cchLine   = pParser->cchLine;
     2889
     2890    if (pParser->iDepth < 1)
     2891        return scmKmkGiveUp(pParser, "Lone 'else'");
     2892    uint32_t const cchIndent = pParser->iActualDepth - !pParser->aDepth[pParser->iDepth].fIgnoreNesting;
     2893
     2894    /*
     2895     * Look past the else and check if there any ifxxx token following it.
     2896     */
     2897    size_t offSrc = offToken + 4;
     2898    while (offSrc < cchLine && RT_C_IS_BLANK(pchLine[offSrc]))
     2899        offSrc++;
     2900    if (offSrc < cchLine)
     2901    {
     2902        size_t cchWord = 0;
     2903        while (offSrc + cchWord < cchLine && RT_C_IS_ALNUM(pchLine[offSrc + cchWord]))
     2904            cchWord++;
     2905        if (cchWord)
     2906        {
     2907            KMKTOKEN enmToken = scmKmkIdentifyToken(&pchLine[offSrc], cchWord);
     2908            switch (enmToken)
     2909            {
     2910                case kKmkToken_ifeq:
     2911                case kKmkToken_ifneq:
     2912                case kKmkToken_if1of:
     2913                case kKmkToken_ifn1of:
     2914                    return scmKmkHandleIfParentheses(pParser, offSrc, enmToken, cchWord, true /*fElse*/);
     2915
     2916                case kKmkToken_ifdef:
     2917                case kKmkToken_ifndef:
     2918                case kKmkToken_if:
     2919                    return scmKmkHandleIfSpace(pParser, offSrc, enmToken, cchWord, true /*fElse*/);
     2920
     2921                default:
     2922                    break;
     2923            }
     2924        }
     2925    }
     2926
     2927    /*
     2928     * We do not allow line continuation for these.
     2929     */
     2930    if (scmKmkIsLineWithContinuation(pchLine, cchLine))
     2931        return scmKmkGiveUp(pParser, "Line continuation not allowed with 'else' directive.");
     2932
     2933    /*
     2934     * We stage the modified line in the buffer, so check that the line isn't
     2935     * too long (it seriously should be).
     2936     */
     2937    if (cchLine + cchIndent + 32 > sizeof(pParser->szBuf))
     2938        return scmKmkGiveUp(pParser, "Line too long for a 'else' directive: %u chars", cchLine);
     2939    char *pszDst = pParser->szBuf;
     2940
     2941    /*
     2942     * Emit indent and initial token.
     2943     */
     2944    memset(pszDst, ' ', cchIndent);
     2945    pszDst = (char *)mempcpy(&pszDst[cchIndent], RT_STR_TUPLE("else"));
     2946
     2947    offSrc = offToken + 4;
     2948    bool   fModified = offSrc != (size_t)(pszDst - &pParser->szBuf[0])
     2949                    || memcmp(pchLine, pszDst, offSrc) != 0;
     2950
     2951    /*
     2952     * Handle comment.
     2953     */
     2954    if (offSrc < cchLine)
     2955        fModified |= scmKmkTailComment(pParser, pchLine, cchLine, offSrc, &pszDst);
     2956
     2957    /*
     2958     * Done.
     2959     */
     2960    *pszDst = '\0';
     2961    ScmStreamPutLine(pParser->pOut, pParser->szBuf, pszDst - pParser->szBuf, pParser->enmEol);
     2962    return fModified;
     2963}
     2964
     2965
     2966/**
     2967 * Deals with: endif
     2968 */
     2969static bool scmKmkHandleEndif(KMKPARSER *pParser, size_t offToken)
     2970{
     2971    const char * const pchLine   = pParser->pchLine;
     2972    size_t  const      cchLine   = pParser->cchLine;
     2973
     2974    /*
     2975     * Pop a nesting.
     2976     */
     2977    if (pParser->iDepth < 1)
     2978        return scmKmkGiveUp(pParser, "Lone 'endif'");
     2979    uint32_t iDepth = pParser->iDepth - 1;
     2980    pParser->iDepth = iDepth;
     2981    if (!pParser->aDepth[iDepth].fIgnoreNesting)
     2982    {
     2983        AssertStmt(pParser->iActualDepth > 0, pParser->iActualDepth++);
     2984        pParser->iActualDepth -= 1;
     2985    }
     2986    uint32_t const cchIndent = pParser->iActualDepth;
     2987
     2988    /*
     2989     * We do not allow line continuation for these.
     2990     */
     2991    if (scmKmkIsLineWithContinuation(pchLine, cchLine))
     2992        return scmKmkGiveUp(pParser, "Line continuation not allowed with 'endif' directive.");
     2993
     2994    /*
     2995     * We stage the modified line in the buffer, so check that the line isn't
     2996     * too long (it seriously should be).
     2997     */
     2998    if (cchLine + cchIndent + 32 > sizeof(pParser->szBuf))
     2999        return scmKmkGiveUp(pParser, "Line too long for a 'else' directive: %u chars", cchLine);
     3000    char *pszDst = pParser->szBuf;
     3001
     3002    /*
     3003     * Emit indent and initial token.
     3004     */
     3005    memset(pszDst, ' ', cchIndent);
     3006    pszDst = (char *)mempcpy(&pszDst[cchIndent], RT_STR_TUPLE("endif"));
     3007
     3008    size_t offSrc    = offToken + 5;
     3009    bool   fModified = offSrc != (size_t)(pszDst - &pParser->szBuf[0])
     3010                    || memcmp(pchLine, pszDst, offSrc) != 0;
     3011
     3012    /*
     3013     * Handle comment.
     3014     */
     3015    if (offSrc < cchLine)
     3016        fModified |= scmKmkTailComment(pParser, pchLine, cchLine, offSrc, &pszDst);
     3017
     3018    /*
     3019     * Done.
     3020     */
     3021    *pszDst = '\0';
     3022    ScmStreamPutLine(pParser->pOut, pParser->szBuf, pszDst - pParser->szBuf, pParser->enmEol);
     3023    return fModified;
     3024}
     3025
     3026
    21753027/**
    21763028 * Rewrite a kBuild makefile.
     
    21893041bool rewrite_Makefile_kmk(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
    21903042{
    2191     RT_NOREF4(pState, pIn, pOut, pSettings);
    2192     return false;
    2193 }
    2194 
     3043    if (!pSettings->fStandarizeKmk)
     3044        return false;
     3045
     3046    /*
     3047     * Parser state.
     3048     */
     3049    KMKPARSER Parser;
     3050    Parser.iDepth       = 0;
     3051    Parser.iActualDepth = 0;
     3052    Parser.fInReceipt   = false;
     3053    Parser.iLine        = 0;
     3054    Parser.pState       = pState;
     3055    Parser.pIn          = pIn;
     3056    Parser.pOut         = pOut;
     3057    Parser.pSettings    = pSettings;
     3058
     3059    /*
     3060     * Iterate the file.
     3061     */
     3062    bool        fModified = false;
     3063    const char *pchLine;
     3064    while ((Parser.pchLine = pchLine = ScmStreamGetLine(pIn, &Parser.cchLine, &Parser.enmEol)) != NULL)
     3065    {
     3066        Parser.iLine++;
     3067        size_t const cchLine = Parser.cchLine;
     3068
     3069        /*
     3070         * Skip leading whitespace and check for directives (simplified).
     3071         */
     3072        size_t offLine = 0;
     3073        while (offLine < cchLine && RT_C_IS_BLANK(pchLine[offLine]))
     3074            offLine++;
     3075
     3076        /* Find end of word (if any): */
     3077        size_t cchWord = 0;
     3078        while (offLine + cchWord < cchLine && (RT_C_IS_ALNUM(pchLine[offLine + cchWord]) || pchLine[offLine + cchWord] == '-'))
     3079            cchWord++;
     3080        if (cchWord > 0)
     3081        {
     3082            KMKTOKEN enmToken = scmKmkIdentifyToken(&pchLine[offLine], cchWord);
     3083            switch (enmToken)
     3084            {
     3085                case kKmkToken_ifeq:
     3086                case kKmkToken_ifneq:
     3087                case kKmkToken_if1of:
     3088                case kKmkToken_ifn1of:
     3089                    fModified |= scmKmkHandleIfParentheses(&Parser, offLine, enmToken, cchWord, false /*fElse*/);
     3090                    continue;
     3091
     3092                case kKmkToken_ifdef:
     3093                case kKmkToken_ifndef:
     3094                case kKmkToken_if:
     3095                    fModified |= scmKmkHandleIfSpace(&Parser, offLine, enmToken, cchWord, false /*fElse*/);
     3096                    continue;
     3097
     3098                case kKmkToken_else:
     3099                    fModified |= scmKmkHandleElse(&Parser, offLine);
     3100                    continue;
     3101
     3102                case kKmkToken_endif:
     3103                    fModified |= scmKmkHandleEndif(&Parser, offLine);
     3104                    continue;
     3105
     3106                /* Includes: */
     3107                case kKmkToken_include:
     3108                case kKmkToken_sinclude:
     3109                case kKmkToken_dash_include:
     3110                case kKmkToken_includedep:
     3111                case kKmkToken_includedep_queue:
     3112                case kKmkToken_includedep_flush:
     3113                    break;
     3114
     3115                /* Others: */
     3116                case kKmkToken_define:
     3117                case kKmkToken_endef:
     3118                case kKmkToken_export:
     3119                case kKmkToken_unexport:
     3120                case kKmkToken_local:
     3121                case kKmkToken_override:
     3122                    break;
     3123
     3124                case kKmkToken_Word:
     3125                    break;
     3126            }
     3127        }
     3128
     3129        /* Pass it thru as-is: */
     3130        ScmStreamPutLine(pOut, pchLine, cchLine, Parser.enmEol);
     3131    }
     3132
     3133    return fModified;
     3134}
     3135
     3136
     3137
     3138/*********************************************************************************************************************************
     3139*   Flower Box Section Markers                                                                                                   *
     3140*********************************************************************************************************************************/
    21953141
    21963142static bool isFlowerBoxSectionMarker(PSCMSTREAM pIn, const char *pchLine, size_t cchLine, uint32_t cchWidth,
  • trunk/src/bldprogs/scmstream.cpp

    r98103 r98319  
    917917    memcpy(pvBuf, &pStream->pch[pStream->off], cbToRead);
    918918    return ScmStreamSeekAbsolute(pStream, pStream->off + cbToRead);
     919}
     920
     921
     922/**
     923 * Checks if we're at the end of the stream.
     924 *
     925 * @returns true if end of stream, false if not.
     926 * @param   pStream             The stream.  Must be in read mode.
     927 */
     928bool ScmStreamIsEndOfStream(PSCMSTREAM pStream)
     929{
     930    AssertReturn(!pStream->fWriteOrRead, false);
     931    return pStream->off >= pStream->cb
     932        || RT_FAILURE(pStream->rc);
    919933}
    920934
  • trunk/src/bldprogs/scmstream.h

    r98103 r98319  
    129129unsigned    ScmStreamPeekCh(PSCMSTREAM pStream);
    130130int         ScmStreamRead(PSCMSTREAM pStream, void *pvBuf, size_t cbToRead);
     131bool        ScmStreamIsEndOfStream(PSCMSTREAM pStream);
    131132bool        ScmStreamIsWhiteLine(PSCMSTREAM pStream, size_t iLine);
    132133SCMEOL      ScmStreamGetEol(PSCMSTREAM pStream);
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette