From f03c437e2fe0d669d1e64dcedd305d5bd8ca0608 Mon Sep 17 00:00:00 2001 From: "Joseph C. Sible" Date: Wed, 24 May 2023 16:38:53 -0400 Subject: [PATCH 01/10] Get rid of a dangerous partial function from checkSpacefulnessCfg' --- src/ShellCheck/Analytics.hs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/ShellCheck/Analytics.hs b/src/ShellCheck/Analytics.hs index 1f6d96d..ecc170d 100644 --- a/src/ShellCheck/Analytics.hs +++ b/src/ShellCheck/Analytics.hs @@ -2134,7 +2134,8 @@ checkSpacefulnessCfg' dirtyPass params token@(T_DollarBraced id _ list) = addDoubleQuotesAround params token where - name = getBracedReference $ concat $ oversimplify list + bracedString = concat $ oversimplify list + name = getBracedReference bracedString parents = parentMap params needsQuoting = not (isArrayExpansion token) -- There's another warning for this @@ -2153,14 +2154,10 @@ checkSpacefulnessCfg' dirtyPass params token@(T_DollarBraced id _ list) = || CF.spaceStatus (CF.variableValue state) == CF.SpaceStatusClean isDefaultAssignment parents token = - let modifier = getBracedModifier $ bracedString token in + let modifier = getBracedModifier bracedString in any (`isPrefixOf` modifier) ["=", ":="] && isParamTo parents ":" token - -- Given a T_DollarBraced, return a simplified version of the string contents. - bracedString (T_DollarBraced _ _ l) = concat $ oversimplify l - bracedString _ = error $ pleaseReport "bracedString on non-variable" - checkSpacefulnessCfg' _ _ _ = return () From b625cc1accb3249322ca757b6709840f7582b072 Mon Sep 17 00:00:00 2001 From: Nicolas Theodarus Date: Sun, 28 May 2023 12:33:16 +0200 Subject: [PATCH 02/10] add dependabot.yml --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..81bae9a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 + +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" From 372c0b667e7b6f36a5f1a42a9802eb0246ee3e95 Mon Sep 17 00:00:00 2001 From: Vidar Holen Date: Sun, 30 Jul 2023 13:47:00 -0700 Subject: [PATCH 03/10] SC2324: Warn when x+=1 appends. --- CHANGELOG.md | 1 + src/ShellCheck/ASTLib.hs | 9 +++++++++ src/ShellCheck/Analytics.hs | 38 +++++++++++++++++++++++++++++++++++ src/ShellCheck/CFGAnalysis.hs | 16 +++++++++++++++ 4 files changed, 64 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc1c0ce..8f4426e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## Git ### Added +- SC2324: Warn when x+=1 appends instead of increments. ### Fixed diff --git a/src/ShellCheck/ASTLib.hs b/src/ShellCheck/ASTLib.hs index 56903ee..64fa762 100644 --- a/src/ShellCheck/ASTLib.hs +++ b/src/ShellCheck/ASTLib.hs @@ -886,6 +886,15 @@ isUnmodifiedParameterExpansion t = in getBracedReference str == str _ -> False +-- Return the referenced variable if (and only if) it's an unmodified parameter expansion. +getUnmodifiedParameterExpansion t = + case t of + T_DollarBraced _ _ list -> do + let str = concat $ oversimplify list + guard $ getBracedReference str == str + return str + _ -> Nothing + --- A list of the element and all its parents up to the root node. getPath tree t = t : case Map.lookup (getId t) tree of diff --git a/src/ShellCheck/Analytics.hs b/src/ShellCheck/Analytics.hs index ecc170d..dbad0f5 100644 --- a/src/ShellCheck/Analytics.hs +++ b/src/ShellCheck/Analytics.hs @@ -201,6 +201,7 @@ nodeChecks = [ ,checkOverwrittenExitCode ,checkUnnecessaryArithmeticExpansionIndex ,checkUnnecessaryParens + ,checkPlusEqualsNumber ] optionalChecks = map fst optionalTreeChecks @@ -5007,5 +5008,42 @@ checkUnnecessaryParens params t = ] +prop_checkPlusEqualsNumber1 = verify checkPlusEqualsNumber "x+=1" +prop_checkPlusEqualsNumber2 = verify checkPlusEqualsNumber "x+=42" +prop_checkPlusEqualsNumber3 = verifyNot checkPlusEqualsNumber "(( x += 1 ))" +prop_checkPlusEqualsNumber4 = verifyNot checkPlusEqualsNumber "declare -i x=0; x+=1" +prop_checkPlusEqualsNumber5 = verifyNot checkPlusEqualsNumber "x+='1'" +prop_checkPlusEqualsNumber6 = verifyNot checkPlusEqualsNumber "n=foo; x+=n" +prop_checkPlusEqualsNumber7 = verify checkPlusEqualsNumber "n=4; x+=n" +prop_checkPlusEqualsNumber8 = verify checkPlusEqualsNumber "n=4; x+=$n" +prop_checkPlusEqualsNumber9 = verifyNot checkPlusEqualsNumber "declare -ia var; var[x]+=1" +checkPlusEqualsNumber params t = + case t of + T_Assignment id Append var _ word -> sequence_ $ do + state <- CF.getIncomingState (cfgAnalysis params) id + guard $ isNumber state word + guard . not $ fromMaybe False $ CF.variableMayBeDeclaredInteger state var + return $ warn id 2324 "var+=1 will append, not increment. Use (( var += 1 )), declare -i var, or quote number to silence." + _ -> return () + + where + isNumber state word = + let + unquotedLiteral = getUnquotedLiteral word + isEmpty = unquotedLiteral == Just "" + isUnquotedNumber = not isEmpty && fromMaybe False (all isDigit <$> unquotedLiteral) + isNumericalVariableName = fromMaybe False $ do + str <- unquotedLiteral + CF.variableMayBeAssignedInteger state str + isNumericalVariableExpansion = + case word of + T_NormalWord _ [part] -> fromMaybe False $ do + str <- getUnmodifiedParameterExpansion part + CF.variableMayBeAssignedInteger state str + _ -> False + in + isUnquotedNumber || isNumericalVariableName || isNumericalVariableExpansion + + return [] runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |]) diff --git a/src/ShellCheck/CFGAnalysis.hs b/src/ShellCheck/CFGAnalysis.hs index 4e36cf5..3b4f957 100644 --- a/src/ShellCheck/CFGAnalysis.hs +++ b/src/ShellCheck/CFGAnalysis.hs @@ -59,6 +59,8 @@ module ShellCheck.CFGAnalysis ( ,getIncomingState ,getOutgoingState ,doesPostDominate + ,variableMayBeDeclaredInteger + ,variableMayBeAssignedInteger ,ShellCheck.CFGAnalysis.runTests -- STRIP ) where @@ -153,6 +155,20 @@ doesPostDominate analysis target base = fromMaybe False $ do (targetStart, _) <- M.lookup target $ tokenToRange analysis return $ targetStart `elem` (postDominators analysis ! baseEnd) +-- See if any execution path results in the variable containing a state +variableMayHaveState :: ProgramState -> String -> CFVariableProp -> Maybe Bool +variableMayHaveState state var property = do + value <- M.lookup var $ variablesInScope state + return $ any (S.member property) $ variableProperties value + +-- See if any execution path declares the variable an integer (declare -i). +variableMayBeDeclaredInteger state var = variableMayHaveState state var CFVPInteger + +-- See if any execution path suggests the variable may contain an integer value +variableMayBeAssignedInteger state var = do + value <- M.lookup var $ variablesInScope state + return $ (numericalStatus $ variableValue value) >= NumericalStatusMaybe + getDataForNode analysis node = M.lookup node $ nodeToData analysis -- The current state of data flow at a point in the program, potentially as a diff From 9490b9488627a06e0a4af1c11644b7936b8a2422 Mon Sep 17 00:00:00 2001 From: Vidar Holen Date: Sun, 30 Jul 2023 16:52:40 -0700 Subject: [PATCH 04/10] Save and restore pending here docs when sourcing files (fixes #2803) --- CHANGELOG.md | 3 ++- src/ShellCheck/Checker.hs | 9 +++++++++ src/ShellCheck/Parser.hs | 10 ++++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f4426e..c6c9513 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ ## Git ### Added -- SC2324: Warn when x+=1 appends instead of increments. +- SC2324: Warn when x+=1 appends instead of increments ### Fixed +- source statements with here docs now work correctly ### Changed diff --git a/src/ShellCheck/Checker.hs b/src/ShellCheck/Checker.hs index b56be68..c79f90f 100644 --- a/src/ShellCheck/Checker.hs +++ b/src/ShellCheck/Checker.hs @@ -508,5 +508,14 @@ prop_rcCanSuppressEarlyProblems2 = null result csScript = "!/bin/bash\necho 'hello world'" } +prop_sourceWithHereDocWorks = null result + where + result = checkWithIncludes [("bar", "true\n")] "source bar << eof\nlol\neof" + +prop_hereDocsAreParsedWithoutTrailingLinefeed = 1044 `elem` result + where + result = check "cat << eof" + + return [] runTests = $quickCheckAll diff --git a/src/ShellCheck/Parser.hs b/src/ShellCheck/Parser.hs index 9d7df00..341a435 100644 --- a/src/ShellCheck/Parser.hs +++ b/src/ShellCheck/Parser.hs @@ -2283,8 +2283,13 @@ readSource t@(T_Redirecting _ _ (T_SimpleCommand cmdId _ (cmd:file':rest'))) = d subRead name script = withContext (ContextSource name) $ - inSeparateContext $ - subParse (initialPos name) (readScriptFile True) script + inSeparateContext $ do + oldState <- getState + setState $ oldState { pendingHereDocs = [] } + result <- subParse (initialPos name) (readScriptFile True) script + newState <- getState + setState $ newState { pendingHereDocs = pendingHereDocs oldState } + return result readSource t = return t @@ -3322,6 +3327,7 @@ readScriptFile sourced = do then do commands <- readCompoundListOrEmpty id <- endSpan start + readPendingHereDocs verifyEof let script = T_Annotation annotationId annotations $ T_Script id shebang commands From dd747b2a98c3214978a97b9ee0ec38e635b6e621 Mon Sep 17 00:00:00 2001 From: Vidar Holen Date: Sun, 30 Jul 2023 19:18:27 -0700 Subject: [PATCH 05/10] SC2325/SC2326: Warn about ! ! foo and foo | ! bar (fixes #2810) --- CHANGELOG.md | 2 ++ src/ShellCheck/Checks/ShellSupport.hs | 26 ++++++++++++++++++++++++++ src/ShellCheck/Parser.hs | 22 +++++++++++++++------- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6c9513..0338f7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## Git ### Added - SC2324: Warn when x+=1 appends instead of increments +- SC2325: Warn about multiple `!`s in dash/sh. +- SC2326: Warn about `foo | ! bar` in bash/dash/sh. ### Fixed - source statements with here docs now work correctly diff --git a/src/ShellCheck/Checks/ShellSupport.hs b/src/ShellCheck/Checks/ShellSupport.hs index cf8acc9..c7ece1a 100644 --- a/src/ShellCheck/Checks/ShellSupport.hs +++ b/src/ShellCheck/Checks/ShellSupport.hs @@ -60,6 +60,8 @@ checks = [ ,checkBraceExpansionVars ,checkMultiDimensionalArrays ,checkPS1Assignments + ,checkMultipleBangs + ,checkBangAfterPipe ] testChecker (ForShell _ t) = @@ -566,5 +568,29 @@ checkPS1Assignments = ForShell [Bash] f escapeRegex = mkRegex "\\\\x1[Bb]|\\\\e|\x1B|\\\\033" +prop_checkMultipleBangs1 = verify checkMultipleBangs "! ! true" +prop_checkMultipleBangs2 = verifyNot checkMultipleBangs "! true" +checkMultipleBangs = ForShell [Dash, Sh] f + where + f token = case token of + T_Banged id (T_Banged _ _) -> + err id 2325 "Multiple ! in front of pipelines are a bash/ksh extension. Use only 0 or 1." + _ -> return () + + +prop_checkBangAfterPipe1 = verify checkBangAfterPipe "true | ! true" +prop_checkBangAfterPipe2 = verifyNot checkBangAfterPipe "true | ( ! true )" +prop_checkBangAfterPipe3 = verifyNot checkBangAfterPipe "! ! true | true" +checkBangAfterPipe = ForShell [Dash, Sh, Bash] f + where + f token = case token of + T_Pipeline _ _ cmds -> mapM_ check cmds + _ -> return () + + check token = case token of + T_Banged id _ -> + err id 2326 "! is not allowed in the middle of pipelines. Use command group as in cmd | { ! cmd; } if necessary." + _ -> return () + return [] runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |]) diff --git a/src/ShellCheck/Parser.hs b/src/ShellCheck/Parser.hs index 341a435..ffc58e2 100644 --- a/src/ShellCheck/Parser.hs +++ b/src/ShellCheck/Parser.hs @@ -2296,14 +2296,18 @@ readSource t = return t prop_readPipeline = isOk readPipeline "! cat /etc/issue | grep -i ubuntu" prop_readPipeline2 = isWarning readPipeline "!cat /etc/issue | grep -i ubuntu" prop_readPipeline3 = isOk readPipeline "for f; do :; done|cat" +prop_readPipeline4 = isOk readPipeline "! ! true" +prop_readPipeline5 = isOk readPipeline "true | ! true" readPipeline = do unexpecting "keyword/token" readKeyword - do - (T_Bang id) <- g_Bang - pipe <- readPipeSequence - return $ T_Banged id pipe - <|> - readPipeSequence + readBanged readPipeSequence + +readBanged parser = do + pos <- getPosition + (T_Bang id) <- g_Bang + next <- readBanged parser + return $ T_Banged id next + <|> parser prop_readAndOr = isOk readAndOr "grep -i lol foo || exit 1" prop_readAndOr1 = isOk readAndOr "# shellcheck disable=1\nfoo" @@ -2359,7 +2363,7 @@ readTerm = do readPipeSequence = do start <- startSpan - (cmds, pipes) <- sepBy1WithSeparators readCommand + (cmds, pipes) <- sepBy1WithSeparators (readBanged readCommand) (readPipe `thenSkip` (spacing >> readLineBreak)) id <- endSpan start spacing @@ -2389,6 +2393,10 @@ readCommand = choice [ ] readCmdName = do + -- If the command name is `!` then + optional . lookAhead . try $ do + char '!' + whitespace -- Ignore alias suppression optional . try $ do char '\\' From e3d8483e49c62b7009e72407c8e56424ebb58a0f Mon Sep 17 00:00:00 2001 From: Matt Jolly Date: Tue, 28 Feb 2023 20:30:31 +1100 Subject: [PATCH 06/10] Rebase of chromiumos fork https://chromium.googlesource.com/chromiumos/third_party/shellcheck/ --- ShellCheck.cabal | 1 + portage/get_vars.py | 128 +++ portage/get_vars_diff.py | 120 +++ shellcheck.1.md | 5 +- src/ShellCheck/ASTLib.hs | 20 +- src/ShellCheck/Analytics.hs | 162 +++- src/ShellCheck/AnalyzerLib.hs | 39 + src/ShellCheck/CFGAnalysis.hs | 2 +- src/ShellCheck/Checker.hs | 7 +- src/ShellCheck/Checks/Commands.hs | 72 +- src/ShellCheck/Data.hs | 117 ++- src/ShellCheck/Interface.hs | 24 +- .../PortageAutoInternalVariables.hs | 823 ++++++++++++++++++ 13 files changed, 1479 insertions(+), 41 deletions(-) create mode 100644 portage/get_vars.py create mode 100644 portage/get_vars_diff.py create mode 100644 src/ShellCheck/PortageAutoInternalVariables.hs diff --git a/ShellCheck.cabal b/ShellCheck.cabal index f09521f..f9c8c76 100644 --- a/ShellCheck.cabal +++ b/ShellCheck.cabal @@ -97,6 +97,7 @@ library ShellCheck.Regex other-modules: Paths_ShellCheck + ShellCheck.PortageAutoInternalVariables default-language: Haskell98 executable shellcheck diff --git a/portage/get_vars.py b/portage/get_vars.py new file mode 100644 index 0000000..58258c9 --- /dev/null +++ b/portage/get_vars.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright 2019 The ChromiumOS Authors +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google LLC nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# Binary License Terms + +"""Extract eclass variable names into Haskell list format.""" +from __future__ import print_function +import datetime +import os +import re +import sys +import textwrap +# Matches a line that declares a variable in an eclass. +VAR_RE = re.compile(r'@(?:ECLASS-)?VARIABLE:\s*(\w+)$') +# Matches a line that declares inheritance. +INHERIT_RE = re.compile(r'^[^#]*\binherit((?:\s+[\w-]+)+)$') +VAR_FILE_HEADER = """module ShellCheck.PortageAutoInternalVariables ( + portageAutoInternalVariables + ) where +-- This file contains the variables generated by +-- portage/get_vars.py""" +PORTAGE_AUTO_VAR_NAME = 'portageAutoInternalVariables' +class Eclass: + """Container for eclass information""" + def __init__(self, name, eclass_vars, inheritances): + self.name = name + self.vars = eclass_vars + self.inheritances = inheritances + def calculate_eclass_vars(self, eclasses): + while self.inheritances: + name = self.inheritances.pop() + try: + sub_eclass = eclasses[name] + new_vars = sub_eclass.calculate_eclass_vars(eclasses).vars + self.vars = self.vars.union(new_vars) + except Exception: + pass + return self +def print_var_list(eclass, eclass_vars): + var_list = ' '.join(['"%s",' % v for v in sorted(eclass_vars)]) + print(' -- %s\n%s' % + (eclass, + textwrap.fill( + var_list, 80, initial_indent=' ', subsequent_indent=' '))) +def process_file(eclass_path): + eclass_name = os.path.splitext(os.path.basename(eclass_path))[0] + with open(eclass_path, 'r') as f: + eclass_vars = set() + eclass_inheritances = set() + for line in f: + line = line.strip() + if not line: + continue + while line[-1] == '\\': + line = line[:-1] + next(f).strip() + match = VAR_RE.search(line) + if match: + var_name = match.group(1) + eclass_vars.add(var_name.strip()) + else: + match = INHERIT_RE.search(line) + if match: + for inheritance in re.split(r'\s+', match.group(1)): + if inheritance.strip(): + eclass_inheritances.add(inheritance.strip()) + return Eclass(eclass_name, eclass_vars, eclass_inheritances) +def format_eclasses_as_haskell_map(eclasses): + map_entries = [] + join_string = '", "' + for value in sorted(eclasses, key=(lambda x: x.name)): + if value.vars: + var_list_string = f'"{join_string.join(sorted(list(value.vars)))}"' + map_entries.append( + textwrap.fill( + f'("{value.name}", [{var_list_string}])', + 80, + initial_indent=' ', + subsequent_indent=' ')) + return_string = ',\n\n'.join(map_entries) + return_string = f""" Data.Map.fromList + [ +{return_string} + ]""" + return f"""{VAR_FILE_HEADER}\n\n +-- Last Generated: {datetime.datetime.now().strftime("%x")} +import qualified Data.Map +{PORTAGE_AUTO_VAR_NAME} = +{return_string}""" +def main(argv): + eclasses = {} + for path in sorted(argv, key=os.path.basename): + if not path.endswith('.eclass'): + continue + new_eclass = process_file(path) + eclasses[new_eclass.name] = new_eclass + eclasses_list = [ + value.calculate_eclass_vars(eclasses) for key, value in eclasses.items() + ] + print(format_eclasses_as_haskell_map(eclasses_list)) +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/portage/get_vars_diff.py b/portage/get_vars_diff.py new file mode 100644 index 0000000..7351698 --- /dev/null +++ b/portage/get_vars_diff.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +# Copyright 2020 The ChromiumOS Authors +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: + +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google LLC nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# Binary License Terms + +"""Generates diff of vars from get_vars.py and those existing in Data.hs.""" +import itertools +from pathlib import Path +import subprocess +SCRIPT = Path(__file__).resolve() +THIRD_PARTY = SCRIPT.parent.parent.parent.parent.parent +# List of relative directories in which to find the eclasses. +eclass_rel_dirs = ( + THIRD_PARTY / 'chromiumos-overlay' / 'eclass', + THIRD_PARTY / 'portage-stable' / 'eclass', + THIRD_PARTY / 'eclass-overlay' / 'eclass', +) +# Runs get_vars.py with the eclass paths and store the output. +cmd = [SCRIPT.with_name('get_vars.py')] + list( + itertools.chain(*(x.glob('*') for x in eclass_rel_dirs))) +new_output = subprocess.check_output(cmd, encoding='utf-8').splitlines() +new = [] +for line in new_output: + if '--' in line: + new.append(line.strip()) + elif not line.strip(): + continue + else: + new += (line.replace('"', '').replace('\n', '').split(',')) +# Reads the Data.hs relevant area and store the lines. +data_hs = THIRD_PARTY / 'shellcheck' / 'src' / 'ShellCheck' / 'Data.hs' +with data_hs.open('r', encoding='utf-8') as fp: + record = False + old = [] + for line in fp: + if line.strip() == '-- autotest.eclass declared incorrectly': + break + if line.strip() == '-- generic ebuilds': + record = True + if record: + if '--' in line: + old.append(line.strip()) + elif not line.strip(): + continue + else: + old += line.replace('"', '').replace('\n', '').split(',') +# Cleans up empty bits as a result of parsing difficulties. +new = [x.strip() for x in new if x.strip()] +old = [x.strip() for x in old if x.strip()] +all_eclasses = set() +old_vars = {} +new_vars = {} +current_eclass = '' +for item in old: + if '--' in item: + # It's an eclass comment line. + current_eclass = item[3:] + all_eclasses.add(current_eclass) + continue + else: + # It's a var, so add it to the dict of the current eclass. + old_vars.setdefault(current_eclass, []).append(item) +for item in new: + if '--' in item: + # It's an eclass comment line. + current_eclass = item[3:] + all_eclasses.add(current_eclass) + continue + else: + # It's a var, so add it to the dict of the current eclass. + new_vars.setdefault(current_eclass, []).append(item) +for eclass in sorted(all_eclasses): + if eclass in old_vars: + if eclass not in new_vars: + # Checks if the entire eclass is removed. + print(f'{eclass} not present in new variables.') + for var in old_vars[eclass]: + print(f'\t-{var}') + print() + else: + # Eclass isn't removed, so check for added or removed vars. + toprint = '\n'.join( + [f'\t-{x}' for x in old_vars[eclass] if x not in new_vars[eclass]] + + [f'\t+{x}' for x in new_vars[eclass] if x not in old_vars[eclass]]) + if toprint: + print(eclass) + print(toprint) + if eclass in new_vars: + if eclass not in old_vars: + # Checks if entire eclass is new. + print(f'{eclass} added in new variables.') + for var in new_vars[eclass]: + print(f'\t+{var}') + print() diff --git a/shellcheck.1.md b/shellcheck.1.md index 9675e79..cd7b22a 100644 --- a/shellcheck.1.md +++ b/shellcheck.1.md @@ -87,8 +87,9 @@ not warn at all, as `ksh` supports decimals in arithmetic contexts. : Specify Bourne shell dialect. Valid values are *sh*, *bash*, *dash* and *ksh*. The default is to deduce the shell from the file's `shell` directive, - shebang, or `.bash/.bats/.dash/.ksh` extension, in that order. *sh* refers to - POSIX `sh` (not the system's), and will warn of portability issues. + shebang, or `.bash/.bats/.dash/.ksh/.ebuild/.eclass` extension, in that + order. *sh* refers to POSIX `sh` (not the system's), and will warn of + portability issues. **-S**\ *SEVERITY*,\ **--severity=***severity* diff --git a/src/ShellCheck/ASTLib.hs b/src/ShellCheck/ASTLib.hs index 64fa762..6cf48cb 100644 --- a/src/ShellCheck/ASTLib.hs +++ b/src/ShellCheck/ASTLib.hs @@ -36,8 +36,6 @@ import Numeric (showHex) import Test.QuickCheck -arguments (T_SimpleCommand _ _ (cmd:args)) = args - -- Is this a type of loop? isLoop t = case t of T_WhileExpression {} -> True @@ -559,11 +557,29 @@ getCommandNameFromExpansion t = extract (T_Pipeline _ _ [cmd]) = getCommandName cmd extract _ = Nothing +-- If a command substitution is a single command, get its argument Tokens. +-- Return an empty list if there are no arguments or the token is not a command substitution. +-- $(date +%s) = ["+%s"] +getArgumentsFromExpansion :: Token -> [Token] +getArgumentsFromExpansion t = + case t of + T_DollarExpansion _ [c] -> extract c + T_Backticked _ [c] -> extract c + T_DollarBraceCommandExpansion _ [c] -> extract c + _ -> [] + where + extract (T_Pipeline _ _ [cmd]) = arguments cmd + extract _ = [] + -- Get the basename of a token representing a command getCommandBasename = fmap basename . getCommandName basename = reverse . takeWhile (/= '/') . reverse +-- Get the arguments to a command +arguments (T_SimpleCommand _ _ (cmd:args)) = args +arguments t = maybe [] arguments (getCommand t) + isAssignment t = case t of T_Redirecting _ _ w -> isAssignment w diff --git a/src/ShellCheck/Analytics.hs b/src/ShellCheck/Analytics.hs index dbad0f5..ad7f24c 100644 --- a/src/ShellCheck/Analytics.hs +++ b/src/ShellCheck/Analytics.hs @@ -68,6 +68,7 @@ treeChecks = [ ,checkArrayAssignmentIndices ,checkUseBeforeDefinition ,checkAliasUsedInSameParsingUnit + ,checkForStableKeywordsin9999CrosWorkonEbuilds ,checkArrayValueUsedAsIndex ] @@ -291,6 +292,12 @@ verifyTree f s = producesComments f s == Just True verifyNotTree :: (Parameters -> Token -> [TokenComment]) -> String -> Bool verifyNotTree f s = producesComments f s == Just False +-- Takes a regular checker function and a Parameters and returns a new +-- checker function that acts as though portage mode had been passed +-- in the parameters. +withPortageParams :: (Parameters -> a) -> Parameters -> a +withPortageParams f p = f $ p { portageFileType = Ebuild False } + checkCommand str f t@(T_SimpleCommand id _ (cmd:rest)) | t `isCommand` str = f cmd rest checkCommand _ _ _ = return () @@ -778,6 +785,33 @@ checkFindExec _ cmd@(T_SimpleCommand _ _ t@(h:r)) | cmd `isCommand` "find" = do fromWord _ = [] checkFindExec _ _ = return () +commandNeverProducesSpaces params t = + maybe False (`elem` noSpaceCommands) cmd + || (maybe False (`elem` spacesFromArgsCommands) cmd && noArgsHaveSpaces t) + where + cmd = getCommandNameFromExpansion t + noSpaceCommands = + if isPortageBuild params + then + [ + "usev" + , "use_with" + , "use_enable" + ] + else + [] + spacesFromArgsCommands = + if isPortageBuild params + then + [ + "usex" + , "meson_use" + , "meson_feature" + ] + else + [] + noArgsHaveSpaces t = all (' ' `notElem`) (words $ getArgumentsFromExpansion t) + words = map $ getLiteralStringDef " " prop_checkUnquotedExpansions1 = verify checkUnquotedExpansions "rm $(ls)" prop_checkUnquotedExpansions1a = verify checkUnquotedExpansions "rm `ls`" @@ -790,6 +824,11 @@ prop_checkUnquotedExpansions6 = verifyNot checkUnquotedExpansions "$(cmd)" prop_checkUnquotedExpansions7 = verifyNot checkUnquotedExpansions "cat << foo\n$(ls)\nfoo" prop_checkUnquotedExpansions8 = verifyNot checkUnquotedExpansions "set -- $(seq 1 4)" prop_checkUnquotedExpansions9 = verifyNot checkUnquotedExpansions "echo foo `# inline comment`" +prop_checkUnquotedExpansionsUsev = verify checkUnquotedExpansions "echo $(usev X)" +prop_checkUnquotedExpansionsPortageUsev = verifyNot (withPortageParams checkUnquotedExpansions) "echo $(usev X)" +prop_checkUnquotedExpansionsUsex = verify checkUnquotedExpansions "echo $(usex X)" +prop_checkUnquotedExpansionsPortageUsex1 = verifyNot (withPortageParams checkUnquotedExpansions) "echo $(usex X \"\" Y)" +prop_checkUnquotedExpansionsPortageUsex2 = verify (withPortageParams checkUnquotedExpansions) "echo $(usex X \"Y Z\" W)" prop_checkUnquotedExpansions10 = verify checkUnquotedExpansions "#!/bin/sh\nexport var=$(val)" prop_checkUnquotedExpansions11 = verifyNot checkUnquotedExpansions "ps -p $(pgrep foo)" checkUnquotedExpansions params = @@ -801,7 +840,7 @@ checkUnquotedExpansions params = check _ = return () tree = parentMap params examine t contents = - unless (null contents || shouldBeSplit t || isQuoteFree (shellType params) tree t || usedAsCommandName tree t) $ + unless (null contents || shouldBeSplit t || isQuoteFree (shellType params) tree t || usedAsCommandName tree t || commandNeverProducesSpaces params t) $ warn (getId t) 2046 "Quote this to prevent word splitting." shouldBeSplit t = @@ -963,6 +1002,10 @@ checkArrayAsString _ (T_Assignment id _ _ _ word) = "Brace expansions and globs are literal in assignments. Quote it or use an array." checkArrayAsString _ _ = return () +allArrayVariables params = + shellArrayVariables ++ + if isPortageBuild params then portageArrayVariables else [] + prop_checkArrayWithoutIndex1 = verifyTree checkArrayWithoutIndex "foo=(a b); echo $foo" prop_checkArrayWithoutIndex2 = verifyNotTree checkArrayWithoutIndex "foo='bar baz'; foo=($foo); echo ${foo[0]}" prop_checkArrayWithoutIndex3 = verifyTree checkArrayWithoutIndex "coproc foo while true; do echo cow; done; echo $foo" @@ -978,6 +1021,7 @@ checkArrayWithoutIndex params _ = doVariableFlowAnalysis readF writeF defaultMap (variableFlow params) where defaultMap = Map.fromList $ map (\x -> (x,())) arrayVariables + arrayVariables = allArrayVariables params readF _ (T_DollarBraced id _ token) _ = do map <- get return . maybeToList $ do @@ -1070,6 +1114,9 @@ prop_checkSingleQuotedVariables22 = verifyNot checkSingleQuotedVariables "jq '$_ prop_checkSingleQuotedVariables23 = verifyNot checkSingleQuotedVariables "command jq '$__loc__'" prop_checkSingleQuotedVariables24 = verifyNot checkSingleQuotedVariables "exec jq '$__loc__'" prop_checkSingleQuotedVariables25 = verifyNot checkSingleQuotedVariables "exec -c -a foo jq '$__loc__'" +prop_checkSingleQuotedVariablesCros1 = verifyNot checkSingleQuotedVariables "python_gen_any_dep 'dev-python/pyyaml[${PYTHON_USEDEP}]'" +prop_checkSingleQuotedVariablesCros2 = verifyNot checkSingleQuotedVariables "python_gen_cond_dep 'dev-python/unittest2[${PYTHON_USEDEP}]' python2_7 pypy" +prop_checkSingleQuotedVariablesCros3 = verifyNot checkSingleQuotedVariables "version_format_string '${PN}_source_$1_$2-$3_$4'" checkSingleQuotedVariables params t@(T_SingleQuoted id s) = @@ -1109,6 +1156,9 @@ checkSingleQuotedVariables params t@(T_SingleQuoted id s) = ,"git filter-branch" ,"mumps -run %XCMD" ,"mumps -run LOOP%XCMD" + ,"python_gen_any_dep" + ,"python_gen_cond_dep" + ,"version_format_string" ] || "awk" `isSuffixOf` commandName || "perl" `isPrefixOf` commandName @@ -2036,6 +2086,47 @@ doVariableFlowAnalysis readFunc writeFunc empty flow = evalState ( writeFunc base token name values doFlow _ = return [] +-- Ensure that portage vars without spaces only exist when parsing portage files +allVariablesWithoutSpaces params = + variablesWithoutSpaces ++ + if isPortageBuild params then portageVariablesWithoutSpaces else [] + +getInheritedEclasses :: Token -> [String] +getInheritedEclasses root = execWriter $ doAnalysis findInheritedEclasses root + where + findInheritedEclasses cmd + | cmd `isCommand` "inherit" = tell $ catMaybes $ getLiteralString <$> (arguments cmd) + findInheritedEclasses _ = return () + +checkForStableKeywordsin9999CrosWorkonEbuilds :: Parameters -> Token -> [TokenComment] +checkForStableKeywordsin9999CrosWorkonEbuilds params root = + if isPortage9999Ebuild params && "cros-workon" `elem` getInheritedEclasses root + then ensureNoStableKeywords root + else [] + +prop_checkEnsureNoStableKeywords1 = verifyNotTree (const ensureNoStableKeywords) "KEYWORDS=\"~*\"" +prop_checkEnsureNoStableKeywords2 = verifyNotTree (const ensureNoStableKeywords) "KEYWORDS=\"-* ~amd64\"" +prop_checkEnsureNoStableKeywords3 = verifyTree (const ensureNoStableKeywords) "KEYWORDS=\"*\"" +prop_checkEnsureNoStableKeywords4 = verifyTree (const ensureNoStableKeywords) "KEYWORDS=\"-* amd64\"" +ensureNoStableKeywords :: Token -> [TokenComment] +ensureNoStableKeywords root = + execWriter $ doAnalysis warnStableKeywords root + +-- warnStableKeywords will emit an error if any KEYWORDS listed in the .ebuild +-- file are marked as stable. In practice, this means that there are any +-- KEYWORDS that do not begin with - or ~. +warnStableKeywords :: Token -> Writer [TokenComment] () +warnStableKeywords (T_Assignment _ Assign "KEYWORDS" [] + (T_NormalWord _ [T_DoubleQuoted id [(T_Literal _ keywords)]])) + | any isStableKeyword (words keywords) = + tell [makeComment ErrorC id 5000 $ + "All KEYWORDS in -9999.ebuild files inheriting from cros-workon must " ++ + "be marked as unstable (~*, ~amd64, etc...), or broken (-*, -amd64, etc...)."] +warnStableKeywords _ = return () + +isStableKeyword :: String -> Bool +isStableKeyword k = not (("~" `isPrefixOf` k) || ("-" `isPrefixOf` k)) + -- Don't suggest quotes if this will instead be autocorrected -- from $foo=bar to foo=bar. This is not pretty but ok. quotesMayConflictWithSC2281 params t = @@ -2317,6 +2408,13 @@ checkFunctionsUsedExternally params t = info definitionId 2032 $ "This function can't be invoked via " ++ cmd ++ patternContext cmdId +allInternalVariables params = + genericInternalVariables ++ + if shellType params == Ksh then kshInternalVariables else [] ++ + if isPortageBuild params + then portageInternalVariables (getInheritedEclasses (rootNode params)) + else [] + prop_checkUnused0 = verifyNotTree checkUnusedAssignments "var=foo; echo $var" prop_checkUnused1 = verifyTree checkUnusedAssignments "var=foo; echo $bar" prop_checkUnused2 = verifyNotTree checkUnusedAssignments "var=foo; export var;" @@ -2366,6 +2464,26 @@ prop_checkUnused44 = verifyNotTree checkUnusedAssignments "DEFINE_string \"foo$i prop_checkUnused45 = verifyTree checkUnusedAssignments "readonly foo=bar" prop_checkUnused46 = verifyTree checkUnusedAssignments "readonly foo=(bar)" prop_checkUnused47 = verifyNotTree checkUnusedAssignments "a=1; alias hello='echo $a'" +prop_checkUnused_portageVarAssign = + verifyNotTree (withPortageParams checkUnusedAssignments) + "BROOT=2" +prop_checkUnused_portageVarAssignNoPortageParams = + verifyTree checkUnusedAssignments + "BROOT=2" +prop_checkUnused_portageInheritedVarAssign = + verifyNotTree (withPortageParams checkUnusedAssignments) + "inherit cargo; CARGO_INSTALL_PATH=2" +prop_checkUnused_portageInheritedVarAssignNoPortage = + verifyTree checkUnusedAssignments + "inherit cargo; CARGO_INSTALL_PATH=2" +prop_checkUnused_portageInheritedVarAssignNoInherit = + verifyTree (withPortageParams checkUnusedAssignments) + "CARGO_INSTALL_PATH=2" +prop_checkUnused_portageInheritedVarAssignNoInheritOrPortage = + verifyTree checkUnusedAssignments + "CARGO_INSTALL_PATH=2" +prop_checkUnusedTcExport = verifyNotTree checkUnusedAssignments "tc-export CC; echo $CC" +prop_checkUnusedTcExportBuildEnv = verifyNotTree checkUnusedAssignments "tc-export_build_env CC; echo $CC $BUILD_CFLAGS $CFLAGS_FOR_BUILD" prop_checkUnused48 = verifyNotTree checkUnusedAssignments "_a=1" prop_checkUnused49 = verifyNotTree checkUnusedAssignments "declare -A array; key=a; [[ -v array[$key] ]]" prop_checkUnused50 = verifyNotTree checkUnusedAssignments "foofunc() { :; }; typeset -fx foofunc" @@ -2393,6 +2511,7 @@ checkUnusedAssignments params t = execWriter (mapM_ warnFor unused) stripSuffix = takeWhile isVariableChar defaultMap = Map.fromList $ zip internalVariables $ repeat () + internalVariables = allInternalVariables params prop_checkUnassignedReferences1 = verifyTree checkUnassignedReferences "echo $foo" prop_checkUnassignedReferences2 = verifyNotTree checkUnassignedReferences "foo=hello; echo $foo" @@ -2443,6 +2562,24 @@ prop_checkUnassignedReferences_minusNBraced = verifyNotTree checkUnassignedRefe prop_checkUnassignedReferences_minusZBraced = verifyNotTree checkUnassignedReferences "if [ -z \"${x}\" ]; then echo \"\"; fi" prop_checkUnassignedReferences_minusNDefault = verifyNotTree checkUnassignedReferences "if [ -n \"${x:-}\" ]; then echo $x; fi" prop_checkUnassignedReferences_minusZDefault = verifyNotTree checkUnassignedReferences "if [ -z \"${x:-}\" ]; then echo \"\"; fi" +prop_checkUnassignedReference_portageVarReference = + verifyNotTree (withPortageParams (checkUnassignedReferences' True)) + "echo $BROOT" +prop_checkUnassignedReference_portageVarReferenceNoPortage = + verifyTree (checkUnassignedReferences' True) + "echo $BROOT" +prop_checkUnassignedReference_portageInheritedVarReference = + verifyNotTree (withPortageParams (checkUnassignedReferences' True)) + "inherit cargo; echo $CARGO_INSTALL_PATH" +prop_checkUnassignedReference_portageInheritedVarReferenceNoPortage = + verifyTree (checkUnassignedReferences' True) + "inherit cargo; echo $CARGO_INSTALL_PATH" +prop_checkUnassignedReference_portageInheritedVarReferenceNoInherit = + verifyTree (withPortageParams (checkUnassignedReferences' True)) + "echo $CARGO_INSTALL_PATH" +prop_checkUnassignedReference_portageInheritedVarReferenceNoInheritOrPortage = + verifyTree (checkUnassignedReferences' True) + "echo $CARGO_INSTALL_PATH" prop_checkUnassignedReferences50 = verifyNotTree checkUnassignedReferences "echo ${foo:+bar}" prop_checkUnassignedReferences51 = verifyNotTree checkUnassignedReferences "echo ${foo:+$foo}" prop_checkUnassignedReferences52 = verifyNotTree checkUnassignedReferences "wait -p pid; echo $pid" @@ -2452,6 +2589,7 @@ checkUnassignedReferences' includeGlobals params t = warnings where (readMap, writeMap) = execState (mapM tally $ variableFlow params) (Map.empty, Map.empty) defaultAssigned = Map.fromList $ map (\a -> (a, ())) $ filter (not . null) internalVariables + internalVariables = allInternalVariables params tally (Assignment (_, _, name, _)) = modify (\(read, written) -> (read, Map.insert name () written)) @@ -3199,6 +3337,7 @@ checkUncheckedCdPushdPopd params root = [_, str] -> str `matches` regex _ -> False regex = mkRegex "^/*((\\.|\\.\\.)/+)*(\\.|\\.\\.)?$" + exit = if isPortageBuild params then "die" else "exit" prop_checkLoopVariableReassignment1 = verify checkLoopVariableReassignment "for i in *; do for i in *.bar; do true; done; done" prop_checkLoopVariableReassignment2 = verify checkLoopVariableReassignment "for i in *; do for((i=0; i<3; i++)); do true; done; done" @@ -3509,6 +3648,18 @@ prop_checkSplittingInArrays5 = verifyNot checkSplittingInArrays "a=( $! $$ $# )" prop_checkSplittingInArrays6 = verifyNot checkSplittingInArrays "a=( ${#arr[@]} )" prop_checkSplittingInArrays7 = verifyNot checkSplittingInArrays "a=( foo{1,2} )" prop_checkSplittingInArrays8 = verifyNot checkSplittingInArrays "a=( * )" +prop_checkSplittingInArraysUseWith1 = verify checkSplittingInArrays "a=( $(use_with b) )" +prop_checkSplittingInArraysUseWith2 = verifyNot (withPortageParams checkSplittingInArrays) "a=( $(use_with b) )" +prop_checkSplittingInArraysUseEnable1 = verify checkSplittingInArrays "a=( `use_enable b` )" +prop_checkSplittingInArraysUseEnable2 = verifyNot (withPortageParams checkSplittingInArrays) "a=( `use_enable b` )" +prop_checkSplittingInArraysMesonUse1 = verify checkSplittingInArrays "a=( `meson_use b` )" +prop_checkSplittingInArraysMesonUse2 = verifyNot (withPortageParams checkSplittingInArrays) "a=( `meson_use b` )" +prop_checkSplittingInArraysMesonFeature1 = verify checkSplittingInArrays "a=( `meson_feature b` )" +prop_checkSplittingInArraysMesonFeature2 = verifyNot (withPortageParams checkSplittingInArrays) "a=( `meson_feature b` )" +prop_checkSplittingInArraysUsex1 = verify checkSplittingInArrays "a=( $(usex X Y Z) )" +prop_checkSplittingInArraysUsex2 = verify (withPortageParams checkSplittingInArrays) "a=( `usex X \"Y Z\" W` )" +prop_checkSplittingInArraysUsex3 = verify (withPortageParams checkSplittingInArrays) "a=( `usex X \"${VAR}\" W` )" +prop_checkSPlittingInArraysUsex4 = verifyNot (withPortageParams checkSplittingInArrays) "a=( `usex X Y Z` )" checkSplittingInArrays params t = case t of T_Array _ elements -> mapM_ check elements @@ -3518,9 +3669,9 @@ checkSplittingInArrays params t = T_NormalWord _ parts -> mapM_ checkPart parts _ -> return () checkPart part = case part of - T_DollarExpansion id _ -> forCommand id - T_DollarBraceCommandExpansion id _ -> forCommand id - T_Backticked id _ -> forCommand id + T_DollarExpansion id str -> forCommand id part + T_DollarBraceCommandExpansion id str -> forCommand id part + T_Backticked id _ -> forCommand id part T_DollarBraced id _ str | not (isCountingReference part) && not (isQuotedAlternativeReference part) @@ -3531,7 +3682,8 @@ checkSplittingInArrays params t = else "Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a." _ -> return () - forCommand id = + forCommand id t = + unless (commandNeverProducesSpaces params t) $ warn id 2207 $ if shellType params == Ksh then "Prefer read -A or while read to split command output (or quote to avoid splitting)." diff --git a/src/ShellCheck/AnalyzerLib.hs b/src/ShellCheck/AnalyzerLib.hs index ca928fd..8105b65 100644 --- a/src/ShellCheck/AnalyzerLib.hs +++ b/src/ShellCheck/AnalyzerLib.hs @@ -102,10 +102,18 @@ data Parameters = Parameters { rootNode :: Token, -- map from token id to start and end position tokenPositions :: Map.Map Id (Position, Position), + -- detailed type of any Portage related file + portageFileType :: PortageFileType, -- Result from Control Flow Graph analysis (including data flow analysis) cfgAnalysis :: CF.CFGAnalysis } deriving (Show) +isPortageBuild :: Parameters -> Bool +isPortageBuild params = portageFileType params /= NonPortageRelated + +isPortage9999Ebuild :: Parameters -> Bool +isPortage9999Ebuild params = portageFileType params == Ebuild { is9999Ebuild = True } + -- TODO: Cache results of common AST ops here data Cache = Cache {} @@ -146,6 +154,15 @@ pScript s = } in runIdentity $ parseScript (mockedSystemInterface []) pSpec +-- For testing. Tries to construct Parameters from a test script allowing for +-- alterations to the AnalysisSpec. +makeTestParams :: String -> (AnalysisSpec -> AnalysisSpec) -> Maybe Parameters +makeTestParams s specModifier = do + let pr = pScript s + prRoot pr + let spec = specModifier $ defaultSpec pr + return $ makeParameters spec + -- For testing. If parsed, returns whether there are any comments producesComments :: Checker -> String -> Maybe Bool producesComments c s = do @@ -225,6 +242,7 @@ makeParameters spec = params parentMap = getParentTree root, variableFlow = getVariableFlow params root, tokenPositions = asTokenPositions spec, + portageFileType = asPortageFileType spec, cfgAnalysis = CF.analyzeControlFlow cfParams root } cfParams = CF.CFGParameters { @@ -582,6 +600,15 @@ getReferencedVariableCommand base@(T_SimpleCommand _ _ (T_NormalWord _ (T_Litera head:_ -> map (\x -> (base, head, x)) $ getVariablesFromLiteralToken head _ -> [] "alias" -> [(base, token, name) | token <- rest, name <- getVariablesFromLiteralToken token] + + -- tc-export makes a list of toolchain variables available, similar to export. + -- Usage tc-export CC CXX + "tc-export" -> concatMap getReference rest + + -- tc-export_build_env exports the listed variables plus a bunch of BUILD_XX variables. + -- Usage tc-export_build_env BUILD_CC + "tc-export_build_env" -> concatMap getReference rest ++ concatMap buildVarReferences portageBuildFlagVariables + _ -> [] where forDeclare = @@ -595,6 +622,7 @@ getReferencedVariableCommand base@(T_SimpleCommand _ _ (T_NormalWord _ (T_Litera getReference t@(T_NormalWord _ [T_Literal _ name]) | not ("-" `isPrefixOf` name) = [(t, t, name)] getReference _ = [] flags = map snd $ getAllFlags base + buildVarReferences var = [(base, base, "BUILD_" ++ var), (base, base, var ++ "_FOR_BUILD")] getReferencedVariableCommand _ = [] @@ -653,6 +681,13 @@ getModifiedVariableCommand base@(T_SimpleCommand id cmdPrefix (T_NormalWord _ (T "DEFINE_integer" -> maybeToList $ getFlagVariable rest "DEFINE_string" -> maybeToList $ getFlagVariable rest + -- tc-export creates all the variables passed to it + "tc-export" -> concatMap getModifierParamString rest + + -- tc-export_build_env creates all the variables passed to it + -- plus several BUILD_ and _FOR_BUILD variables. + "tc-export_build_env" -> concatMap getModifierParamString rest ++ getBuildEnvTokens + _ -> [] where flags = map snd $ getAllFlags base @@ -743,6 +778,10 @@ getModifiedVariableCommand base@(T_SimpleCommand id cmdPrefix (T_NormalWord _ (T return (base, n, "FLAGS_" ++ name, DataString $ SourceExternal) getFlagVariable _ = Nothing + getBuildEnvTokens = concatMap buildVarTokens portageBuildFlagVariables + buildVarTokens var = [(base, base, "BUILD_" ++ var, DataString $ SourceExternal), + (base, base, var ++ "_FOR_BUILD", DataString $ SourceExternal)] + getModifiedVariableCommand _ = [] -- Given a NormalWord like foo or foo[$bar], get foo. diff --git a/src/ShellCheck/CFGAnalysis.hs b/src/ShellCheck/CFGAnalysis.hs index 3b4f957..f9ac65f 100644 --- a/src/ShellCheck/CFGAnalysis.hs +++ b/src/ShellCheck/CFGAnalysis.hs @@ -200,7 +200,7 @@ unreachableState = modified newInternalState { createEnvironmentState :: InternalState createEnvironmentState = do foldl' (flip ($)) newInternalState $ concat [ - addVars Data.internalVariables unknownVariableState, + addVars Data.genericInternalVariables unknownVariableState, addVars Data.variablesWithoutSpaces spacelessVariableState, addVars Data.specialIntegerVariables integerVariableState ] diff --git a/src/ShellCheck/Checker.hs b/src/ShellCheck/Checker.hs index c79f90f..5cd6ae8 100644 --- a/src/ShellCheck/Checker.hs +++ b/src/ShellCheck/Checker.hs @@ -54,7 +54,9 @@ shellFromFilename filename = listToMaybe candidates shellExtensions = [(".ksh", Ksh) ,(".bash", Bash) ,(".bats", Bash) - ,(".dash", Dash)] + ,(".dash", Dash) + ,(".ebuild", Bash) + ,(".eclass", Bash)] -- The `.sh` is too generic to determine the shell: -- We fallback to Bash in this case and emit SC2148 if there is no shebang candidates = @@ -86,7 +88,8 @@ checkScript sys spec = do asCheckSourced = csCheckSourced spec, asExecutionMode = Executed, asTokenPositions = tokenPositions, - asOptionalChecks = getEnableDirectives root ++ csOptionalChecks spec + asOptionalChecks = getEnableDirectives root ++ csOptionalChecks spec, + asPortageFileType = getPortageFileType $ csFilename spec } where as = newAnalysisSpec root let analysisMessages = maybe [] diff --git a/src/ShellCheck/Checks/Commands.hs b/src/ShellCheck/Checks/Commands.hs index 691836f..03911e0 100644 --- a/src/ShellCheck/Checks/Commands.hs +++ b/src/ShellCheck/Checks/Commands.hs @@ -60,8 +60,32 @@ verify :: CommandCheck -> String -> Bool verify f s = producesComments (getChecker [f]) s == Just True verifyNot f s = producesComments (getChecker [f]) s == Just False -commandChecks :: [CommandCheck] -commandChecks = [ +verifyDisabledCheckerInPortage :: String -> Bool +verifyDisabledCheckerInPortage = verifyDisabledCheckerInPortage2 $ + Ebuild { is9999Ebuild = True } + +verifyDisabledCheckerInPortage2 :: PortageFileType -> String -> Bool +verifyDisabledCheckerInPortage2 portageFileType s = fromMaybe False $ do + params <- makeTestParams s portageTypeSpec + testSpec <- makeTestSpec + return $ null $ runChecker params (checker testSpec params) + where + portageTypeSpec spec = spec { + asPortageFileType = portageFileType + } + makeTestSpec = do + let pr = pScript s + prRoot pr + return $ portageTypeSpec $ defaultSpec pr + + +commandCheckWhen :: Bool -> CommandCheck -> CommandCheck +commandCheckWhen predicate commandCheck = if predicate + then commandCheck + else CommandCheck (Exactly "skipped") nullCheck + +commandChecks :: Parameters -> [CommandCheck] +commandChecks params = [ checkTr ,checkFindNameGlob ,checkExpr @@ -84,7 +108,7 @@ commandChecks = [ ,checkAliasesUsesArgs ,checkAliasesExpandEarly ,checkUnsetGlobs - ,checkFindWithoutPath + ,commandCheckWhen (not $ isPortageBuild params) checkFindWithoutPath ,checkTimeParameters ,checkTimedCommand ,checkLocalScope @@ -92,7 +116,7 @@ commandChecks = [ ,checkDeprecatedEgrep ,checkDeprecatedFgrep ,checkWhileGetoptsCase - ,checkCatastrophicRm + ,checkCatastrophicRm (isPortageBuild params) ,checkLetUsage ,checkMvArguments, checkCpArguments, checkLnArguments ,checkFindRedirections @@ -206,7 +230,7 @@ getChecker list = Checker { checker :: AnalysisSpec -> Parameters -> Checker -checker spec params = getChecker $ commandChecks ++ optionals +checker spec params = getChecker $ (commandChecks params) ++ optionals where keys = asOptionalChecks spec optionals = @@ -893,6 +917,7 @@ prop_checkFindWithoutPath5 = verifyNot checkFindWithoutPath "find -O3 ." prop_checkFindWithoutPath6 = verifyNot checkFindWithoutPath "find -D exec ." prop_checkFindWithoutPath7 = verifyNot checkFindWithoutPath "find --help" prop_checkFindWithoutPath8 = verifyNot checkFindWithoutPath "find -Hx . -print" +prop_checkFindWithoutPathPortage = verifyDisabledCheckerInPortage "find -type f" checkFindWithoutPath = CommandCheck (Basename "find") f where f t@(T_SimpleCommand _ _ (cmd:args)) = @@ -1071,20 +1096,23 @@ checkWhileGetoptsCase = CommandCheck (Exactly "getopts") f T_Redirecting _ _ x@(T_CaseExpression {}) -> return x _ -> Nothing -prop_checkCatastrophicRm1 = verify checkCatastrophicRm "rm -r $1/$2" -prop_checkCatastrophicRm2 = verify checkCatastrophicRm "rm -r /home/$foo" -prop_checkCatastrophicRm3 = verifyNot checkCatastrophicRm "rm -r /home/${USER:?}/*" -prop_checkCatastrophicRm4 = verify checkCatastrophicRm "rm -fr /home/$(whoami)/*" -prop_checkCatastrophicRm5 = verifyNot checkCatastrophicRm "rm -r /home/${USER:-thing}/*" -prop_checkCatastrophicRm6 = verify checkCatastrophicRm "rm --recursive /etc/*$config*" -prop_checkCatastrophicRm8 = verify checkCatastrophicRm "rm -rf /home" -prop_checkCatastrophicRm10 = verifyNot checkCatastrophicRm "rm -r \"${DIR}\"/{.gitignore,.gitattributes,ci}" -prop_checkCatastrophicRm11 = verify checkCatastrophicRm "rm -r /{bin,sbin}/$exec" -prop_checkCatastrophicRm12 = verify checkCatastrophicRm "rm -r /{{usr,},{bin,sbin}}/$exec" -prop_checkCatastrophicRm13 = verifyNot checkCatastrophicRm "rm -r /{{a,b},{c,d}}/$exec" -prop_checkCatastrophicRmA = verify checkCatastrophicRm "rm -rf /usr /lib/nvidia-current/xorg/xorg" -prop_checkCatastrophicRmB = verify checkCatastrophicRm "rm -rf \"$STEAMROOT/\"*" -checkCatastrophicRm = CommandCheck (Basename "rm") $ \t -> +prop_checkCatastrophicRm1 = verify (checkCatastrophicRm False) "rm -r $1/$2" +prop_checkCatastrophicRm2 = verify (checkCatastrophicRm False) "rm -r /home/$foo" +prop_checkCatastrophicRm3 = verifyNot (checkCatastrophicRm False) "rm -r /home/${USER:?}/*" +prop_checkCatastrophicRm4 = verify (checkCatastrophicRm False) "rm -fr /home/$(whoami)/*" +prop_checkCatastrophicRm5 = verifyNot (checkCatastrophicRm False) "rm -r /home/${USER:-thing}/*" +prop_checkCatastrophicRm6 = verify (checkCatastrophicRm False) "rm --recursive /etc/*$config*" +prop_checkCatastrophicRm8 = verify (checkCatastrophicRm False) "rm -rf /home" +prop_checkCatastrophicRm10 = verifyNot (checkCatastrophicRm False) "rm -r \"${DIR}\"/{.gitignore,.gitattributes,ci}" +prop_checkCatastrophicRm11 = verify (checkCatastrophicRm False) "rm -r /{bin,sbin}/$exec" +prop_checkCatastrophicRm12 = verify (checkCatastrophicRm False) "rm -r /{{usr,},{bin,sbin}}/$exec" +prop_checkCatastrophicRm13 = verifyNot (checkCatastrophicRm False) "rm -r /{{a,b},{c,d}}/$exec" +prop_checkCatastrophicRmA = verify (checkCatastrophicRm False) "rm -rf /usr /lib/nvidia-current/xorg/xorg" +prop_checkCatastrophicRmB = verify (checkCatastrophicRm False) "rm -rf \"$STEAMROOT/\"*" +prop_checkCatastrophicRmED1 = verify (checkCatastrophicRm False) "rm -rf \"$ED/var/\"*" +prop_checkCatastrophicRmED2 = verifyNot (checkCatastrophicRm True) "rm -rf \"$ED/var/\"*" +checkCatastrophicRm isPortageBuild = CommandCheck (Basename "rm") $ \t -> + when (isRecursive t) $ mapM_ (mapM_ checkWord . braceExpand) $ arguments t where @@ -1114,7 +1142,7 @@ checkCatastrophicRm = CommandCheck (Basename "rm") $ \t -> f (T_DollarBraced _ _ word) = let var = onlyLiteralString word in -- This shouldn't handle non-colon cases. - if any (`isInfixOf` var) [":?", ":-", ":="] + if any (`isInfixOf` var) [":?", ":-", ":="] || (isPortageBuild && var `elem` ["D", "ED"]) then Nothing else return "" f _ = return "" @@ -1339,6 +1367,7 @@ checkMaskedReturns str = CommandCheck (Exactly str) checkCmd checkCmd t = do path <- getPathM t shell <- asks shellType + portageFileType <- asks portageFileType sequence_ $ do name <- getCommandName t @@ -1349,10 +1378,11 @@ checkMaskedReturns str = CommandCheck (Exactly str) checkCmd let isLocal = not hasDashG && isLocalInFunction name && isInScopedFunction let isReadOnly = name == "readonly" || hasDashR + let isPortageBuild = portageFileType /= NonPortageRelated -- Don't warn about local variables that are declared readonly, -- because the workaround `local x; x=$(false); local -r x;` is annoying - guard . not $ isLocal && isReadOnly + guard . not $ isLocal && isReadOnly || isPortageBuild return $ mapM_ checkArgs $ arguments t diff --git a/src/ShellCheck/Data.hs b/src/ShellCheck/Data.hs index 550ff87..6a7ab6c 100644 --- a/src/ShellCheck/Data.hs +++ b/src/ShellCheck/Data.hs @@ -1,6 +1,8 @@ module ShellCheck.Data where +import qualified Data.Map import ShellCheck.Interface +import ShellCheck.PortageAutoInternalVariables import Data.Version (showVersion) @@ -24,7 +26,7 @@ import Paths_ShellCheck (version) shellcheckVersion = showVersion version -- VERSIONSTRING -internalVariables = [ +genericInternalVariables = [ -- Generic "", "_", "rest", "REST", @@ -55,15 +57,108 @@ internalVariables = [ "USER", "TZ", "TERM", "LOGNAME", "LD_LIBRARY_PATH", "LANGUAGE", "DISPLAY", "HOSTNAME", "KRB5CCNAME", "XAUTHORITY" - -- Ksh - , ".sh.version" - -- shflags , "FLAGS_ARGC", "FLAGS_ARGV", "FLAGS_ERROR", "FLAGS_FALSE", "FLAGS_HELP", "FLAGS_PARENT", "FLAGS_RESERVED", "FLAGS_TRUE", "FLAGS_VERSION", "flags_error", "flags_return" ] +kshInternalVariables = [ + ".sh.version" + ] + +portageManualInternalVariables = [ + -- toolchain settings + "CFLAGS", "CXXFLAGS", "CPPFLAGS", "LDFLAGS", "FFLAGS", "FCFLAGS", + "CBUILD", "CHOST", "MAKEOPTS" + -- TODO: Delete these if we can handle `tc-export CC` implicit export. + , "CC", "CPP", "CXX" + + -- portage internals + , "EBUILD_PHASE", "EBUILD_SH_ARGS", "EMERGE_FROM", "FILESDIR", + "MERGE_TYPE", "PM_EBUILD_HOOK_DIR", "PORTAGE_ACTUAL_DISTDIR", + "PORTAGE_ARCHLIST", "PORTAGE_BASHRC", "PORTAGE_BINPKG_FILE", + "PORTAGE_BINPKG_TAR_OPTS", "PORTAGE_BINPKG_TMPFILE", "PORTAGE_BIN_PATH", + "PORTAGE_BUILDDIR", "PORTAGE_BUILD_GROUP", "PORTAGE_BUILD_USER", + "PORTAGE_BUNZIP2_COMMAND", "PORTAGE_BZIP2_COMMAND", "PORTAGE_COLORMAP", + "PORTAGE_CONFIGROOT", "PORTAGE_DEBUG", "PORTAGE_DEPCACHEDIR", + "PORTAGE_EBUILD_EXIT_FILE", "PORTAGE_ECLASS_LOCATIONS", "PORTAGE_GID", + "PORTAGE_GRPNAME", "PORTAGE_INST_GID", "PORTAGE_INST_UID", + "PORTAGE_INTERNAL_CALLER", "PORTAGE_IPC_DAEMON", "PORTAGE_IUSE", + "PORTAGE_LOG_FILE", "PORTAGE_MUTABLE_FILTERED_VARS", + "PORTAGE_OVERRIDE_EPREFIX", "PORTAGE_PYM_PATH", "PORTAGE_PYTHON", + "PORTAGE_PYTHONPATH", "PORTAGE_READONLY_METADATA", "PORTAGE_READONLY_VARS", + "PORTAGE_REPO_NAME", "PORTAGE_REPOSITORIES", "PORTAGE_RESTRICT", + "PORTAGE_SAVED_READONLY_VARS", "PORTAGE_SIGPIPE_STATUS", "PORTAGE_TMPDIR", + "PORTAGE_UPDATE_ENV", "PORTAGE_USERNAME", "PORTAGE_VERBOSE", + "PORTAGE_WORKDIR_MODE", "PORTAGE_XATTR_EXCLUDE", "REPLACING_VERSIONS", + "REPLACED_BY_VERSION", "__PORTAGE_HELPER", "__PORTAGE_TEST_HARDLINK_LOCKS", + + -- generic ebuilds + "A", "ARCH", "BDEPEND", "BOARD_USE", "BROOT", "CATEGORY", "D", + "DEFINED_PHASES", "DEPEND", "DESCRIPTION", "DISTDIR", "DOCS", "EAPI", + "ECLASS", "ED", "EPREFIX", "EROOT", "ESYSROOT", "EXTRA_ECONF", + "EXTRA_EINSTALL", "EXTRA_MAKE", "FEATURES", "FILESDIR", "HOME", "HOMEPAGE", + "HTML_DOCS", "INHERITED", "IUSE", "KEYWORDS", "LICENSE", "P", "PATCHES", + "PDEPEND", "PF", "PKG_INSTALL_MASK", "PKGUSE", "PN", "PR", "PROPERTIES", + "PROVIDES_EXCLUDE", "PV", "PVR", "QA_AM_MAINTAINER_MODE", + "QA_CONFIGURE_OPTIONS", "QA_DESKTOP_FILE", "QA_DT_NEEDED", "QA_EXECSTACK", + "QA_FLAGS_IGNORED", "QA_MULTILIB_PATHS", "QA_PREBUILT", "QA_PRESTRIPPED", + "QA_SONAME", "QA_SONAME_NO_SYMLINK", "QA_TEXTRELS", "QA_WX_LOAD", "RDEPEND", + "REPOSITORY", "REQUIRED_USE", "REQUIRES_EXCLUDE", "RESTRICT", "ROOT", "S", + "SLOT", "SRC_TEST", "SRC_URI", "STRIP_MASK", "SUBSLOT", "SYSROOT", "T", + "WORKDIR", + + -- autotest.eclass declared incorrectly + "AUTOTEST_CLIENT_TESTS", "AUTOTEST_CLIENT_SITE_TESTS", + "AUTOTEST_SERVER_TESTS", "AUTOTEST_SERVER_SITE_TESTS", "AUTOTEST_CONFIG", + "AUTOTEST_DEPS", "AUTOTEST_PROFILERS", "AUTOTEST_CONFIG_LIST", + "AUTOTEST_DEPS_LIST", "AUTOTEST_PROFILERS_LIST", + + -- cros-board.eclass declared incorrectly + "CROS_BOARDS", + + -- Undeclared cros-kernel2 vars + "AFDO_PROFILE_VERSION", + + -- haskell-cabal.eclass declared incorrectly + "CABAL_FEATURES", + + -- Undeclared haskell-cabal.eclass vars + "CABAL_CORE_LIB_GHC_PV", + + -- Undeclared readme.gentoo.eclass vars + "DOC_CONTENTS", + + -- Backwards compatibility perl-module.eclass vars + "MODULE_AUTHOR", "MODULE_VERSION", + + -- Undeclared perl-module.eclass vars + "mydoc", + + -- python-utils-r1.eclass declared incorrectly + "RESTRICT_PYTHON_ABIS", "PYTHON_MODNAME", + + -- ABI variables + "ABI", "DEFAULT_ABI", + + -- AFDO variables + "AFDO_LOCATION", + + -- Linguas + "LINGUAS" + ] + +eclassVarsFromMap :: String -> [String] +eclassVarsFromMap eclass = + Data.Map.findWithDefault [] + eclass + portageAutoInternalVariables + +portageInternalVariables inheritedEclasses = + portageManualInternalVariables ++ concatMap eclassVarsFromMap + inheritedEclasses + specialIntegerVariables = [ "$", "?", "!", "#" ] @@ -81,18 +176,30 @@ variablesWithoutSpaces = specialVariablesWithoutSpaces ++ [ , "FLAGS_ERROR", "FLAGS_FALSE", "FLAGS_TRUE" ] +portageVariablesWithoutSpaces = [ + "EAPI", "P", "PF", "PN", "PR", "PV", "PVR", "SLOT" + ] + specialVariables = specialVariablesWithoutSpaces ++ ["@", "*"] unbracedVariables = specialVariables ++ [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] -arrayVariables = [ +shellArrayVariables = [ "BASH_ALIASES", "BASH_ARGC", "BASH_ARGV", "BASH_CMDS", "BASH_LINENO", "BASH_REMATCH", "BASH_SOURCE", "BASH_VERSINFO", "COMP_WORDS", "COPROC", "DIRSTACK", "FUNCNAME", "GROUPS", "MAPFILE", "PIPESTATUS", "COMPREPLY" ] +portageArrayVariables = [ + "PATCHES" + ] + +portageBuildFlagVariables = [ + "CFLAGS", "CXXFLAGS", "CPPFLAGS", "LDFLAGS" + ] + commonCommands = [ "admin", "alias", "ar", "asa", "at", "awk", "basename", "batch", "bc", "bg", "break", "c99", "cal", "cat", "cd", "cflow", "chgrp", diff --git a/src/ShellCheck/Interface.hs b/src/ShellCheck/Interface.hs index 7528559..60a9b94 100644 --- a/src/ShellCheck/Interface.hs +++ b/src/ShellCheck/Interface.hs @@ -25,7 +25,7 @@ module ShellCheck.Interface , CheckResult(crFilename, crComments) , ParseSpec(psFilename, psScript, psCheckSourced, psIgnoreRC, psShellTypeOverride) , ParseResult(prComments, prTokenPositions, prRoot) - , AnalysisSpec(asScript, asShellType, asFallbackShell, asExecutionMode, asCheckSourced, asTokenPositions, asOptionalChecks) + , AnalysisSpec(asScript, asShellType, asFallbackShell, asExecutionMode, asCheckSourced, asTokenPositions, asOptionalChecks, asPortageFileType) , AnalysisResult(arComments) , FormatterOptions(foColorOption, foWikiLinkCount) , Shell(Ksh, Sh, Bash, Dash) @@ -58,6 +58,8 @@ module ShellCheck.Interface , newReplacement , CheckDescription(cdName, cdDescription, cdPositive, cdNegative) , newCheckDescription + , PortageFileType(NonPortageRelated, Ebuild, is9999Ebuild, Eclass) + , getPortageFileType ) where import ShellCheck.AST @@ -157,6 +159,20 @@ newParseResult = ParseResult { prRoot = Nothing } +data PortageFileType = NonPortageRelated + | Ebuild { is9999Ebuild :: Bool } + | Eclass deriving (Show, Eq) + +getPortageFileType :: String -> PortageFileType +getPortageFileType filename + | ".ebuild" `isSuffixOf` filename = ebuildType + | ".eclass" `isSuffixOf` filename = Eclass + | otherwise = NonPortageRelated + where + ebuildType = Ebuild { + is9999Ebuild = "-9999.ebuild" `isSuffixOf` filename + } + -- Analyzer input and output data AnalysisSpec = AnalysisSpec { asScript :: Token, @@ -165,7 +181,8 @@ data AnalysisSpec = AnalysisSpec { asExecutionMode :: ExecutionMode, asCheckSourced :: Bool, asOptionalChecks :: [String], - asTokenPositions :: Map.Map Id (Position, Position) + asTokenPositions :: Map.Map Id (Position, Position), + asPortageFileType :: PortageFileType } newAnalysisSpec token = AnalysisSpec { @@ -175,7 +192,8 @@ newAnalysisSpec token = AnalysisSpec { asExecutionMode = Executed, asCheckSourced = False, asOptionalChecks = [], - asTokenPositions = Map.empty + asTokenPositions = Map.empty, + asPortageFileType = NonPortageRelated } newtype AnalysisResult = AnalysisResult { diff --git a/src/ShellCheck/PortageAutoInternalVariables.hs b/src/ShellCheck/PortageAutoInternalVariables.hs new file mode 100644 index 0000000..e5558e7 --- /dev/null +++ b/src/ShellCheck/PortageAutoInternalVariables.hs @@ -0,0 +1,823 @@ +module ShellCheck.PortageAutoInternalVariables ( + portageAutoInternalVariables + ) where + +-- This file contains the variables generated by +-- portage/get_vars.py + + +-- Last Generated: 12/02/22 + +import qualified Data.Map + +portageAutoInternalVariables = + Data.Map.fromList + [ + ("alternatives", ["ALTERNATIVES", "SOURCE"]), + + ("apache-2", ["AM_OPTS", "AT_M4DIR", "AT_NOEAUTOMAKE", "AT_NOELIBTOOLIZE", + "AT_SYS_M4DIR", "AUTOTOOLS_AUTO_DEPEND", "EPATCH_COMMON_OPTS", + "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", + "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "GENTOO_DEVELOPER", + "GENTOO_PATCHDIR", "GENTOO_PATCHNAME", "GENTOO_PATCHSTAMP", + "GENTOO_PATCH_A", "IUSE_MODULES", "IUSE_MPMS_FORK", "IUSE_MPMS_THREAD", + "MODULE_CRITICAL", "MODULE_DEFINES", "MODULE_DEPENDS", "MY_CONF", "MY_MODS", + "MY_MPM", "WANT_AUTOCONF", "WANT_AUTOMAKE", "WANT_LIBTOOL", + "_LATEST_AUTOMAKE"]), + + ("arc-build", ["ARC_BASE", "ARC_ETC_DIR", "ARC_LLVM_VERSION", "ARC_PREFIX", + "ARC_VENDOR_DIR", "ARC_VERSION_MAJOR", "ARC_VERSION_MINOR", + "ARC_VERSION_PATCH", "AUTOTEST_BASE", "BUILD_DIR", "CHROMITE_BIN_DIR", + "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", "CROS_GIT_AOSP_URL", + "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", "EPATCH_COMMON_OPTS", + "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", + "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "MULTIBUILD_ID", + "MULTIBUILD_VARIANT", "MULTIBUILD_VARIANTS", "MULTILIB_ABI_FLAG", + "MULTILIB_CHOST_TOOLS", "MULTILIB_COMPAT", "MULTILIB_USEDEP", + "MULTILIB_WRAPPED_HEADERS", "_MULTILIB_FLAGS"]), + + ("autotest", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", + "AUTOTEST_BASE", "AUTOTEST_FILE_MASK", "AUTOTEST_FORCE_LIST", + "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", + "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL"]), + + ("autotest-deponly", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", + "AUTOTEST_BASE", "AUTOTEST_FILE_MASK", "AUTOTEST_FORCE_LIST", + "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", + "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL"]), + + ("autotest-external-dep", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", + "AUTOTEST_BASE", "AUTOTEST_FILE_MASK", "AUTOTEST_FORCE_LIST", + "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", + "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", + "PACKAGE"]), + + ("autotools", ["AM_OPTS", "AT_M4DIR", "AT_NOEAUTOMAKE", "AT_NOELIBTOOLIZE", + "AT_SYS_M4DIR", "AUTOTOOLS_AUTO_DEPEND", "WANT_AUTOCONF", "WANT_AUTOMAKE", + "WANT_LIBTOOL", "_LATEST_AUTOMAKE"]), + + ("autotools-multilib", ["AM_OPTS", "AT_M4DIR", "AT_NOEAUTOMAKE", + "AT_NOELIBTOOLIZE", "AT_SYS_M4DIR", "AUTOTOOLS_AUTORECONF", + "AUTOTOOLS_AUTO_DEPEND", "AUTOTOOLS_IN_SOURCE_BUILD", + "AUTOTOOLS_PRUNE_LIBTOOL_FILES", "BUILD_DIR", "DOCS", "ECONF_SOURCE", + "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", + "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "HTML_DOCS", "MULTIBUILD_ID", + "MULTIBUILD_VARIANT", "MULTIBUILD_VARIANTS", "MULTILIB_ABI_FLAG", + "MULTILIB_CHOST_TOOLS", "MULTILIB_COMPAT", "MULTILIB_USEDEP", + "MULTILIB_WRAPPED_HEADERS", "PATCHES", "WANT_AUTOCONF", "WANT_AUTOMAKE", + "WANT_LIBTOOL", "_LATEST_AUTOMAKE", "_MULTILIB_FLAGS", "myeconfargs"]), + + ("autotools-utils", ["AM_OPTS", "AT_M4DIR", "AT_NOEAUTOMAKE", + "AT_NOELIBTOOLIZE", "AT_SYS_M4DIR", "AUTOTOOLS_AUTORECONF", + "AUTOTOOLS_AUTO_DEPEND", "AUTOTOOLS_IN_SOURCE_BUILD", + "AUTOTOOLS_PRUNE_LIBTOOL_FILES", "BUILD_DIR", "DOCS", "ECONF_SOURCE", + "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", + "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "HTML_DOCS", "PATCHES", + "WANT_AUTOCONF", "WANT_AUTOMAKE", "WANT_LIBTOOL", "_LATEST_AUTOMAKE", + "myeconfargs"]), + + ("base", ["DOCS", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "HTML_DOCS", + "PATCHES"]), + + ("bash-completion", ["BASHCOMPFILES", "BASHCOMPLETION_NAME", + "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", + "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("cargo", ["CARGO_INSTALL_PATH"]), + + ("cbi-image", ["CROS_CBI_IMAGE_DIR", "EEPROM_SIZE"]), + + ("chromium-source", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", + "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", + "CHROMIUM_GCLIENT_TEMPLATE", "CHROOT_SOURCE_ROOT", "CROS_GIT_AOSP_URL", + "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", "DEPOT_TOOLS", "EGCLIENT", + "ENINJA"]), + + ("cmake", ["BUILD_DIR", "CMAKE_BINARY", "CMAKE_BUILD_TYPE", + "CMAKE_EXTRA_CACHE_FILE", "CMAKE_IN_SOURCE_BUILD", + "CMAKE_MAKEFILE_GENERATOR", "CMAKE_QA_SRC_DIR_READONLY", + "CMAKE_REMOVE_MODULES_LIST", "CMAKE_USE_DIR", "CMAKE_VERBOSE", + "CMAKE_WARN_UNUSED_CLI", "DESKTOP_DATABASE_DIR", "EPATCH_COMMON_OPTS", + "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", + "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "MIMEINFO_DATABASE_DIR", + "NINJAOPTS", "mycmakeargs"]), + + ("cmake-multilib", ["CMAKE_ECLASS"]), + + ("cmake-utils", ["BUILD_DIR", "CMAKE_BINARY", "CMAKE_BUILD_TYPE", + "CMAKE_EXTRA_CACHE_FILE", "CMAKE_IN_SOURCE_BUILD", + "CMAKE_MAKEFILE_GENERATOR", "CMAKE_MIN_VERSION", "CMAKE_REMOVE_MODULES", + "CMAKE_REMOVE_MODULES_LIST", "CMAKE_USE_DIR", + "CMAKE_UTILS_QA_SRC_DIR_READONLY", "CMAKE_VERBOSE", "CMAKE_WARN_UNUSED_CLI", + "DESKTOP_DATABASE_DIR", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", + "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", + "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", + "EPATCH_USER_SOURCE", "MIMEINFO_DATABASE_DIR", "NINJAOPTS", "mycmakeargs"]), + + ("common-lisp", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("common-lisp-common", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", + "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", + "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", + "EPATCH_USER_SOURCE"]), + + ("coreboot-sdk", ["COREBOOT_SDK_PREFIX", "COREBOOT_SDK_PREFIX_arm", + "COREBOOT_SDK_PREFIX_arm64", "COREBOOT_SDK_PREFIX_mips", + "COREBOOT_SDK_PREFIX_nds32", "COREBOOT_SDK_PREFIX_riscv", + "COREBOOT_SDK_PREFIX_x86_32", "COREBOOT_SDK_PREFIX_x86_64"]), + + ("crashid", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("cros-arm64", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", + "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", + "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", + "CROS_WORKON_ALWAYS_LIVE", "CROS_WORKON_COMMIT", "CROS_WORKON_DESTDIR", + "CROS_WORKON_EGIT_BRANCH", "CROS_WORKON_INCREMENTAL_BUILD", + "CROS_WORKON_INPLACE", "CROS_WORKON_LOCALNAME", + "CROS_WORKON_MAKE_COMPILE_ARGS", "CROS_WORKON_MANUAL_UPREV", + "CROS_WORKON_OPTIONAL_CHECKOUT", "CROS_WORKON_OUTOFTREE_BUILD", + "CROS_WORKON_PROJECT", "CROS_WORKON_REPO", "CROS_WORKON_SRCPATH", + "CROS_WORKON_SRCROOT", "CROS_WORKON_SUBDIRS_TO_COPY", + "CROS_WORKON_SUBDIRS_TO_REV", "CROS_WORKON_SUBTREE", "CROS_WORKON_TREE", + "CROS_WORKON_USE_VCSID", "EGIT_BOOTSTRAP", "EGIT_BRANCH", "EGIT_COMMIT", + "EGIT_DIR", "EGIT_HAS_SUBMODULES", "EGIT_MASTER", "EGIT_NONBARE", + "EGIT_NOUNPACK", "EGIT_OPTIONS", "EGIT_PROJECT", "EGIT_PRUNE", + "EGIT_REPACK", "EGIT_REPO_URI", "EGIT_SOURCEDIR", "EGIT_STORE_DIR", + "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", + "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EVCS_OFFLINE"]), + + ("cros-bazel", ["BAZEL_BAZELRC", "BAZEL_CC_BAZELRC", "BAZEL_CC_BUILD", + "BAZEL_CC_CONFIG_DIR", "BAZEL_CC_TOOLCHAIN_CONFIG", + "BAZEL_PORTAGE_PACKAGE_DIR"]), + + ("cros-binary", ["CROS_BINARY_LOCAL_URI_BASE", "CROS_BINARY_URI"]), + + ("cros-common.mk", ["CROS_COMMON_MK_NATIVE_TEST"]), + + ("cros-config-bsp", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", + "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", + "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", + "PROGRAM", "PROJECTS", "UNIBOARD_CROS_CONFIG_DIR", + "UNIBOARD_JSON_INSTALL_PATH", "UNIBOARD_YAML_CONFIG", "UNIBOARD_YAML_DIR"]), + + ("cros-constants", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", + "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", + "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL"]), + + ("cros-cpfe", ["CROS_CPFE_BOARD_OVERLAY", "CROS_CPFE_HOME", + "CROS_CPFE_OVERLAY_NAME", "CROS_CPFE_PATH", "CROS_CPFE_URL", + "CROS_CPFE_USER_NAME"]), + + ("cros-debug", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("cros-ec", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", "AUTOTEST_BASE", + "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", + "COREBOOT_SDK_PREFIX", "COREBOOT_SDK_PREFIX_arm", + "COREBOOT_SDK_PREFIX_arm64", "COREBOOT_SDK_PREFIX_mips", + "COREBOOT_SDK_PREFIX_nds32", "COREBOOT_SDK_PREFIX_riscv", + "COREBOOT_SDK_PREFIX_x86_32", "COREBOOT_SDK_PREFIX_x86_64", + "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", + "CROS_WORKON_ALWAYS_LIVE", "CROS_WORKON_COMMIT", "CROS_WORKON_DESTDIR", + "CROS_WORKON_EGIT_BRANCH", "CROS_WORKON_INCREMENTAL_BUILD", + "CROS_WORKON_INPLACE", "CROS_WORKON_LOCALNAME", + "CROS_WORKON_MAKE_COMPILE_ARGS", "CROS_WORKON_MANUAL_UPREV", + "CROS_WORKON_OPTIONAL_CHECKOUT", "CROS_WORKON_OUTOFTREE_BUILD", + "CROS_WORKON_PROJECT", "CROS_WORKON_REPO", "CROS_WORKON_SRCPATH", + "CROS_WORKON_SRCROOT", "CROS_WORKON_SUBDIRS_TO_COPY", + "CROS_WORKON_SUBDIRS_TO_REV", "CROS_WORKON_SUBTREE", "CROS_WORKON_TREE", + "CROS_WORKON_USE_VCSID", "EC_BOARDS", "EGIT_BOOTSTRAP", "EGIT_BRANCH", + "EGIT_COMMIT", "EGIT_DIR", "EGIT_HAS_SUBMODULES", "EGIT_MASTER", + "EGIT_NONBARE", "EGIT_NOUNPACK", "EGIT_OPTIONS", "EGIT_PROJECT", + "EGIT_PRUNE", "EGIT_REPACK", "EGIT_REPO_URI", "EGIT_SOURCEDIR", + "EGIT_STORE_DIR", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", + "EVCS_OFFLINE", "UNIBOARD_CROS_CONFIG_DIR", "UNIBOARD_JSON_INSTALL_PATH", + "UNIBOARD_YAML_CONFIG", "UNIBOARD_YAML_DIR"]), + + ("cros-ec-board", ["EC_BOARDS", "UNIBOARD_CROS_CONFIG_DIR", + "UNIBOARD_JSON_INSTALL_PATH", "UNIBOARD_YAML_CONFIG", "UNIBOARD_YAML_DIR"]), + + ("cros-ec-release", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", + "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", + "COREBOOT_SDK_PREFIX", "COREBOOT_SDK_PREFIX_arm", + "COREBOOT_SDK_PREFIX_arm64", "COREBOOT_SDK_PREFIX_mips", + "COREBOOT_SDK_PREFIX_nds32", "COREBOOT_SDK_PREFIX_riscv", + "COREBOOT_SDK_PREFIX_x86_32", "COREBOOT_SDK_PREFIX_x86_64", + "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", + "CROS_WORKON_ALWAYS_LIVE", "CROS_WORKON_COMMIT", "CROS_WORKON_DESTDIR", + "CROS_WORKON_EGIT_BRANCH", "CROS_WORKON_INCREMENTAL_BUILD", + "CROS_WORKON_INPLACE", "CROS_WORKON_LOCALNAME", + "CROS_WORKON_MAKE_COMPILE_ARGS", "CROS_WORKON_MANUAL_UPREV", + "CROS_WORKON_OPTIONAL_CHECKOUT", "CROS_WORKON_OUTOFTREE_BUILD", + "CROS_WORKON_PROJECT", "CROS_WORKON_REPO", "CROS_WORKON_SRCPATH", + "CROS_WORKON_SRCROOT", "CROS_WORKON_SUBDIRS_TO_COPY", + "CROS_WORKON_SUBDIRS_TO_REV", "CROS_WORKON_SUBTREE", "CROS_WORKON_TREE", + "CROS_WORKON_USE_VCSID", "EC_BOARDS", "EGIT_BOOTSTRAP", "EGIT_BRANCH", + "EGIT_COMMIT", "EGIT_DIR", "EGIT_HAS_SUBMODULES", "EGIT_MASTER", + "EGIT_NONBARE", "EGIT_NOUNPACK", "EGIT_OPTIONS", "EGIT_PROJECT", + "EGIT_PRUNE", "EGIT_REPACK", "EGIT_REPO_URI", "EGIT_SOURCEDIR", + "EGIT_STORE_DIR", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", + "EVCS_OFFLINE", "FIRMWARE_EC_BOARD", "FIRMWARE_EC_RELEASE_REPLACE_RO", + "UNIBOARD_CROS_CONFIG_DIR", "UNIBOARD_JSON_INSTALL_PATH", + "UNIBOARD_YAML_CONFIG", "UNIBOARD_YAML_DIR"]), + + ("cros-factory", ["CROS_FACTORY_BOARD_RESOURCES_DIR"]), + + ("cros-factory-board", ["CROS_FACTORY_BOARD_RESOURCES_DIR"]), + + ("cros-firmware", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", + "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", + "CROS_FIRMWARE_BCS_OVERLAY", "CROS_FIRMWARE_EC_IMAGE", + "CROS_FIRMWARE_MAIN_IMAGE", "CROS_FIRMWARE_MAIN_RW_IMAGE", + "CROS_FIRMWARE_PD_IMAGE", "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", + "CROS_GIT_INT_HOST_URL", "CROS_WORKON_ALWAYS_LIVE", "CROS_WORKON_COMMIT", + "CROS_WORKON_DESTDIR", "CROS_WORKON_EGIT_BRANCH", + "CROS_WORKON_INCREMENTAL_BUILD", "CROS_WORKON_INPLACE", + "CROS_WORKON_LOCALNAME", "CROS_WORKON_MAKE_COMPILE_ARGS", + "CROS_WORKON_MANUAL_UPREV", "CROS_WORKON_OPTIONAL_CHECKOUT", + "CROS_WORKON_OUTOFTREE_BUILD", "CROS_WORKON_PROJECT", "CROS_WORKON_REPO", + "CROS_WORKON_SRCPATH", "CROS_WORKON_SRCROOT", "CROS_WORKON_SUBDIRS_TO_COPY", + "CROS_WORKON_SUBDIRS_TO_REV", "CROS_WORKON_SUBTREE", "CROS_WORKON_TREE", + "CROS_WORKON_USE_VCSID", "EGIT_BOOTSTRAP", "EGIT_BRANCH", "EGIT_COMMIT", + "EGIT_DIR", "EGIT_HAS_SUBMODULES", "EGIT_MASTER", "EGIT_NONBARE", + "EGIT_NOUNPACK", "EGIT_OPTIONS", "EGIT_PROJECT", "EGIT_PRUNE", + "EGIT_REPACK", "EGIT_REPO_URI", "EGIT_SOURCEDIR", "EGIT_STORE_DIR", + "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", + "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EVCS_OFFLINE", + "UNIBOARD_CROS_CONFIG_DIR", "UNIBOARD_JSON_INSTALL_PATH", + "UNIBOARD_YAML_CONFIG", "UNIBOARD_YAML_DIR"]), + + ("cros-fuzzer", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("cros-fwupd", ["CROS_FWUPD_URL"]), + + ("cros-go", ["CROS_GO_BINARIES", "CROS_GO_PACKAGES", + "CROS_GO_SKIP_DEP_CHECK", "CROS_GO_SOURCE", "CROS_GO_TEST", + "CROS_GO_VERSION", "CROS_GO_VET", "CROS_GO_VET_FLAGS", "CROS_GO_WORKSPACE"]), + + ("cros-i686", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", + "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", + "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", + "CROS_WORKON_ALWAYS_LIVE", "CROS_WORKON_COMMIT", "CROS_WORKON_DESTDIR", + "CROS_WORKON_EGIT_BRANCH", "CROS_WORKON_INCREMENTAL_BUILD", + "CROS_WORKON_INPLACE", "CROS_WORKON_LOCALNAME", + "CROS_WORKON_MAKE_COMPILE_ARGS", "CROS_WORKON_MANUAL_UPREV", + "CROS_WORKON_OPTIONAL_CHECKOUT", "CROS_WORKON_OUTOFTREE_BUILD", + "CROS_WORKON_PROJECT", "CROS_WORKON_REPO", "CROS_WORKON_SRCPATH", + "CROS_WORKON_SRCROOT", "CROS_WORKON_SUBDIRS_TO_COPY", + "CROS_WORKON_SUBDIRS_TO_REV", "CROS_WORKON_SUBTREE", "CROS_WORKON_TREE", + "CROS_WORKON_USE_VCSID", "EGIT_BOOTSTRAP", "EGIT_BRANCH", "EGIT_COMMIT", + "EGIT_DIR", "EGIT_HAS_SUBMODULES", "EGIT_MASTER", "EGIT_NONBARE", + "EGIT_NOUNPACK", "EGIT_OPTIONS", "EGIT_PROJECT", "EGIT_PRUNE", + "EGIT_REPACK", "EGIT_REPO_URI", "EGIT_SOURCEDIR", "EGIT_STORE_DIR", + "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", + "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EVCS_OFFLINE"]), + + ("cros-ish", ["COREBOOT_SDK_PREFIX", "COREBOOT_SDK_PREFIX_arm", + "COREBOOT_SDK_PREFIX_arm64", "COREBOOT_SDK_PREFIX_mips", + "COREBOOT_SDK_PREFIX_nds32", "COREBOOT_SDK_PREFIX_riscv", + "COREBOOT_SDK_PREFIX_x86_32", "COREBOOT_SDK_PREFIX_x86_64", "ISH_TARGETS", + "UNIBOARD_CROS_CONFIG_DIR", "UNIBOARD_JSON_INSTALL_PATH", + "UNIBOARD_YAML_CONFIG", "UNIBOARD_YAML_DIR"]), + + ("cros-kernel-info", ["CONFIG_CHECK", "KBUILD_OUTPUT", "KERNEL_DIR", + "KV_DIR", "KV_EXTRA", "KV_FULL", "KV_LOCAL", "KV_MAJOR", "KV_MINOR", + "KV_OUT_DIR", "KV_PATCH", "_LINUX_CONFIG_EXISTS_DONE"]), + + ("cros-racc", ["CROS_RACC_MODEL"]), + + ("cros-rust", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", + "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", + "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", + "CROS_RUST_CRATE_NAME", "CROS_RUST_CRATE_VERSION", "CROS_RUST_EMPTY_CRATE", + "CROS_RUST_EMPTY_CRATE_FEATURES", "CROS_RUST_HOST_TESTS", + "CROS_RUST_OVERFLOW_CHECKS", "CROS_RUST_PACKAGE_IS_HOT", + "CROS_RUST_PLATFORM_TEST_ARGS", "CROS_RUST_PREINSTALLED_REGISTRY_CRATE", + "CROS_RUST_REMOVE_DEV_DEPS", "CROS_RUST_REMOVE_TARGET_CFG", + "CROS_RUST_SUBDIR", "CROS_RUST_TESTS", "CROS_RUST_TEST_DIRECT_EXEC_ONLY", + "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", + "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("cros-rustc", ["CROS_RUSTC_BUILD_RAW_SOURCES", "EPATCH_COMMON_OPTS", + "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", + "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EPYTHON", "PYTHON", + "PYTHON_COMPAT", "PYTHON_COMPAT_NO_STRICT", "PYTHON_COMPAT_OVERRIDE", + "PYTHON_DEPS", "PYTHON_REQ_USE", "PYTHON_USEDEP", + "RUSTC_BARE_TARGET_TRIPLES", "RUSTC_TARGET_TRIPLES", "_PYTHON_ALL_IMPLS"]), + + ("cros-sanitizers", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("cros-unibuild", ["UNIBOARD_CROS_CONFIG_DIR", "UNIBOARD_JSON_INSTALL_PATH", + "UNIBOARD_YAML_CONFIG", "UNIBOARD_YAML_DIR"]), + + ("cros-workon", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", + "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", + "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", + "CROS_WORKON_ALWAYS_LIVE", "CROS_WORKON_COMMIT", "CROS_WORKON_DESTDIR", + "CROS_WORKON_EGIT_BRANCH", "CROS_WORKON_INCREMENTAL_BUILD", + "CROS_WORKON_INPLACE", "CROS_WORKON_LOCALNAME", + "CROS_WORKON_MAKE_COMPILE_ARGS", "CROS_WORKON_MANUAL_UPREV", + "CROS_WORKON_OPTIONAL_CHECKOUT", "CROS_WORKON_OUTOFTREE_BUILD", + "CROS_WORKON_PROJECT", "CROS_WORKON_REPO", "CROS_WORKON_SRCPATH", + "CROS_WORKON_SRCROOT", "CROS_WORKON_SUBDIRS_TO_COPY", + "CROS_WORKON_SUBDIRS_TO_REV", "CROS_WORKON_SUBTREE", "CROS_WORKON_TREE", + "CROS_WORKON_USE_VCSID", "EGIT_BOOTSTRAP", "EGIT_BRANCH", "EGIT_COMMIT", + "EGIT_DIR", "EGIT_HAS_SUBMODULES", "EGIT_MASTER", "EGIT_NONBARE", + "EGIT_NOUNPACK", "EGIT_OPTIONS", "EGIT_PROJECT", "EGIT_PRUNE", + "EGIT_REPACK", "EGIT_REPO_URI", "EGIT_SOURCEDIR", "EGIT_STORE_DIR", + "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", + "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EVCS_OFFLINE"]), + + ("cuda", ["CUDA_VERBOSE", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", + "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", + "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", + "EPATCH_USER_SOURCE", "NVCCFLAGS"]), + + ("distutils-r1", ["BUILD_DIR", "DESKTOP_DATABASE_DIR", + "DISTUTILS_ALL_SUBPHASE_IMPLS", "DISTUTILS_IN_SOURCE_BUILD", + "DISTUTILS_OPTIONAL", "DISTUTILS_SINGLE_IMPL", "DISTUTILS_USE_SETUPTOOLS", + "DOCS", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EPYTHON", + "EXAMPLES", "HTML_DOCS", "MIMEINFO_DATABASE_DIR", "MULTIBUILD_ID", + "MULTIBUILD_VARIANT", "MULTIBUILD_VARIANTS", "PATCHES", "PYTHON", + "PYTHON_COMPAT", "PYTHON_COMPAT_NO_STRICT", "PYTHON_COMPAT_OVERRIDE", + "PYTHON_DEPS", "PYTHON_MULTI_USEDEP", "PYTHON_REQUIRED_USE", + "PYTHON_REQ_USE", "PYTHON_SINGLE_USEDEP", "PYTHON_USEDEP", + "_PYTHON_ALL_IMPLS", "mydistutilsargs"]), + + ("dlc", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", "AUTOTEST_BASE", + "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", + "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", + "DLC_CRITICAL_UPDATE", "DLC_DAYS_TO_PURGE", "DLC_DESCRIPTION", + "DLC_ENABLED", "DLC_FACTORY_INSTALL", "DLC_FS_TYPE", "DLC_ID", + "DLC_LOADPIN_VERITY_DIGEST", "DLC_MOUNT_FILE_REQUIRED", "DLC_NAME", + "DLC_PACKAGE", "DLC_PREALLOC_BLOCKS", "DLC_PRELOAD", "DLC_RESERVED", + "DLC_SCALED", "DLC_USED_BY", "DLC_VERSION"]), + + ("elisp", ["BYTECOMPFLAGS", "DOCS", "ELISP_PATCHES", "ELISP_TEXINFO", + "EMACS", "EMACSFLAGS", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", + "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", + "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", + "EPATCH_USER_SOURCE", "NEED_EMACS", "SITEETC", "SITEFILE", "SITELISP"]), + + ("elisp-common", ["BYTECOMPFLAGS", "EMACS", "EMACSFLAGS", "SITEETC", + "SITELISP"]), + + ("emboss", ["AM_OPTS", "AT_M4DIR", "AT_NOEAUTOMAKE", "AT_NOELIBTOOLIZE", + "AT_SYS_M4DIR", "AUTOTOOLS_AUTO_DEPEND", "EBO_DESCRIPTION", + "EBO_EAUTORECONF", "EBO_EXTRA_ECONF", "EPATCH_COMMON_OPTS", + "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", + "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "WANT_AUTOCONF", + "WANT_AUTOMAKE", "WANT_LIBTOOL", "_LATEST_AUTOMAKE"]), + + ("epatch", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("epunt-cxx", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("eutils", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("fcaps", ["FILECAPS"]), + + ("flag-o-matic", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("fortran", ["AM_OPTS", "AT_M4DIR", "AT_NOEAUTOMAKE", "AT_NOELIBTOOLIZE", + "AT_SYS_M4DIR", "AUTOTOOLS_AUTO_DEPEND", "EPATCH_COMMON_OPTS", + "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", + "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "WANT_AUTOCONF", + "WANT_AUTOMAKE", "WANT_LIBTOOL", "_LATEST_AUTOMAKE"]), + + ("fortran-2", ["FORTRAN_NEEDED", "FORTRAN_NEED_OPENMP", "FORTRAN_STANDARD"]), + + ("games", ["DOCS", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "HTML_DOCS", + "PATCHES"]), + + ("git-2", ["EGIT_BOOTSTRAP", "EGIT_BRANCH", "EGIT_COMMIT", "EGIT_DIR", + "EGIT_HAS_SUBMODULES", "EGIT_MASTER", "EGIT_NONBARE", "EGIT_NOUNPACK", + "EGIT_OPTIONS", "EGIT_PROJECT", "EGIT_PRUNE", "EGIT_REPACK", + "EGIT_REPO_URI", "EGIT_SOURCEDIR", "EGIT_STORE_DIR", "EVCS_OFFLINE"]), + + ("git-r3", ["EGIT3_STORE_DIR", "EGIT_BRANCH", "EGIT_CHECKOUT_DIR", + "EGIT_CLONE_TYPE", "EGIT_COMMIT", "EGIT_COMMIT_DATE", "EGIT_MIN_CLONE_TYPE", + "EGIT_MIRROR_URI", "EGIT_REPO_URI", "EGIT_SUBMODULES", "EVCS_OFFLINE", + "EVCS_UMASK"]), + + ("gnome.org", ["GNOME_ORG_MODULE", "GNOME_ORG_PVP", "GNOME_TARBALL_SUFFIX"]), + + ("gnome2", ["AM_OPTS", "AT_M4DIR", "AT_NOEAUTOMAKE", "AT_NOELIBTOOLIZE", + "AT_SYS_M4DIR", "AUTOTOOLS_AUTO_DEPEND", "EPATCH_COMMON_OPTS", + "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", + "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "WANT_AUTOCONF", + "WANT_AUTOMAKE", "WANT_LIBTOOL", "_LATEST_AUTOMAKE"]), + + ("gnome2-utils", ["DESKTOP_DATABASE_DIR", "EPATCH_COMMON_OPTS", + "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", + "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "GCONFTOOL_BIN", + "GLIB_COMPILE_SCHEMAS", "GNOME2_ECLASS_GDK_PIXBUF_LOADERS", + "GNOME2_ECLASS_GLIB_SCHEMAS", "GNOME2_ECLASS_ICONS", + "GNOME2_ECLASS_SCHEMAS", "GNOME2_ECLASS_SCROLLS", "MIMEINFO_DATABASE_DIR", + "SCROLLKEEPER_DIR", "SCROLLKEEPER_UPDATE_BIN"]), + + ("go-module", ["EGO_SUM", "_GOMODULE_GOPROXY_BASEURI", + "_GOMODULE_GOSUM_REVERSE_MAP"]), + + ("golang-base", ["EGO_PN"]), + + ("golang-build", ["EGO_BUILD_FLAGS", "EGO_PN"]), + + ("golang-vcs", ["EGO_PN", "EGO_STORE_DIR", "EPATCH_COMMON_OPTS", + "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", + "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EVCS_OFFLINE", "EVCS_UMASK"]), + + ("golang-vcs-snapshot", ["EGO_PN", "EGO_VENDOR"]), + + ("gtest", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", "AUTOTEST_BASE", + "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", + "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", + "GTEST_METADATA_INSTALL_DIR"]), + + ("haskell-cabal", ["CABAL_DEBUG_LOOSENING", "CABAL_EXTRA_BUILD_FLAGS", + "CABAL_EXTRA_CONFIGURE_FLAGS", "CABAL_EXTRA_TEST_FLAGS", + "CABAL_REPORT_OTHER_BROKEN_PACKAGES", "EPATCH_COMMON_OPTS", + "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", + "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "GHC_BOOTSTRAP_FLAGS"]), + + ("java-ant-2", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", + "JAVA_ANT_CLASSPATH_TAGS", "JAVA_ANT_DISABLE_ANT_CORE_DEP", + "JAVA_ANT_IGNORE_SYSTEM_CLASSES", "JAVA_PKG_ALLOW_VM_CHANGE", + "JAVA_PKG_BSFIX", "JAVA_PKG_BSFIX_ALL", "JAVA_PKG_BSFIX_NAME", + "JAVA_PKG_BSFIX_SOURCE_TAGS", "JAVA_PKG_BSFIX_TARGET_TAGS", + "JAVA_PKG_COMPILERS_CONF", "JAVA_PKG_COMPILER_DIR", "JAVA_PKG_DEBUG", + "JAVA_PKG_E_DEPEND", "JAVA_PKG_FORCE_ANT_TASKS", "JAVA_PKG_FORCE_COMPILER", + "JAVA_PKG_FORCE_VM", "JAVA_PKG_WANT_BOOTCLASSPATH", + "JAVA_PKG_WANT_BUILD_VM", "JAVA_PKG_WANT_SOURCE", "JAVA_PKG_WANT_TARGET", + "JAVA_RM_FILES", "WANT_ANT_TASKS"]), + + ("java-pkg-2", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", + "JAVA_PKG_ALLOW_VM_CHANGE", "JAVA_PKG_COMPILERS_CONF", + "JAVA_PKG_COMPILER_DIR", "JAVA_PKG_DEBUG", "JAVA_PKG_E_DEPEND", + "JAVA_PKG_FORCE_ANT_TASKS", "JAVA_PKG_FORCE_COMPILER", "JAVA_PKG_FORCE_VM", + "JAVA_PKG_IUSE", "JAVA_PKG_WANT_BOOTCLASSPATH", "JAVA_PKG_WANT_BUILD_VM", + "JAVA_PKG_WANT_SOURCE", "JAVA_PKG_WANT_TARGET", "JAVA_RM_FILES"]), + + ("java-pkg-opt-2", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", + "JAVA_PKG_ALLOW_VM_CHANGE", "JAVA_PKG_COMPILERS_CONF", + "JAVA_PKG_COMPILER_DIR", "JAVA_PKG_DEBUG", "JAVA_PKG_E_DEPEND", + "JAVA_PKG_FORCE_ANT_TASKS", "JAVA_PKG_FORCE_COMPILER", "JAVA_PKG_FORCE_VM", + "JAVA_PKG_OPT_USE", "JAVA_PKG_WANT_BOOTCLASSPATH", "JAVA_PKG_WANT_BUILD_VM", + "JAVA_PKG_WANT_SOURCE", "JAVA_PKG_WANT_TARGET", "JAVA_RM_FILES"]), + + ("java-utils-2", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", + "JAVA_PKG_ALLOW_VM_CHANGE", "JAVA_PKG_COMPILERS_CONF", + "JAVA_PKG_COMPILER_DIR", "JAVA_PKG_DEBUG", "JAVA_PKG_E_DEPEND", + "JAVA_PKG_FORCE_ANT_TASKS", "JAVA_PKG_FORCE_COMPILER", "JAVA_PKG_FORCE_VM", + "JAVA_PKG_WANT_BOOTCLASSPATH", "JAVA_PKG_WANT_BUILD_VM", + "JAVA_PKG_WANT_SOURCE", "JAVA_PKG_WANT_TARGET", "JAVA_RM_FILES"]), + + ("java-vm-2", ["EPREFIX", "JAVA_VM_BUILD_ONLY", "JAVA_VM_CONFIG_DIR", + "JAVA_VM_DIR", "JAVA_VM_SYSTEM"]), + + ("kernel-2", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("l10n", ["PLOCALES", "PLOCALE_BACKUP"]), + + ("libchrome", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("linux-info", ["CONFIG_CHECK", "KBUILD_OUTPUT", "KERNEL_DIR", "KV_DIR", + "KV_EXTRA", "KV_FULL", "KV_LOCAL", "KV_MAJOR", "KV_MINOR", "KV_OUT_DIR", + "KV_PATCH", "_LINUX_CONFIG_EXISTS_DONE"]), + + ("linux-mod", ["BUILD_PARAMS", "BUILD_TARGETS", "CONFIG_CHECK", + "ECONF_PARAMS", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", + "KBUILD_OUTPUT", "KERNEL_DIR", "KV_DIR", "KV_EXTRA", "KV_FULL", "KV_LOCAL", + "KV_MAJOR", "KV_MINOR", "KV_OBJ", "KV_OUT_DIR", "KV_PATCH", + "MODULES_OPTIONAL_USE", "MODULE_NAMES", "_LINUX_CONFIG_EXISTS_DONE"]), + + ("llvm", ["LLVM_MAX_SLOT", "_LLVM_KNOWN_SLOTS"]), + + ("llvm.org", ["EGIT3_STORE_DIR", "EGIT_BRANCH", "EGIT_CHECKOUT_DIR", + "EGIT_CLONE_TYPE", "EGIT_COMMIT", "EGIT_COMMIT_DATE", "EGIT_MIN_CLONE_TYPE", + "EGIT_MIRROR_URI", "EGIT_REPO_URI", "EGIT_SUBMODULES", "EVCS_OFFLINE", + "EVCS_UMASK", "LLVM_COMPONENTS", "LLVM_TEST_COMPONENTS", + "_LLVM_MASTER_MAJOR", "_LLVM_SOURCE_TYPE"]), + + ("lua-single", ["ELUA", "LUA", "LUA_COMPAT", "LUA_COMPAT_OVERRIDE", + "LUA_DEPS", "LUA_REQUIRED_USE", "LUA_REQ_USE", "LUA_SINGLE_USEDEP", + "LUA_USEDEP", "_LUA_ALL_IMPLS", "_LUA_HISTORICAL_IMPLS"]), + + ("lua-utils", ["ELUA", "LUA", "_LUA_ALL_IMPLS", "_LUA_HISTORICAL_IMPLS"]), + + ("mercurial", ["EHG_CLONE_CMD", "EHG_OFFLINE", "EHG_PROJECT", + "EHG_PULL_CMD", "EHG_QUIET", "EHG_REPO_URI", "EHG_REVISION", + "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", + "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("meson", ["BUILD_DIR", "EMESON_BUILDTYPE", "EMESON_SOURCE", + "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", + "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EPYTHON", "MYMESONARGS", + "NINJAOPTS", "PYTHON", "PYTHON_COMPAT_NO_STRICT", "_PYTHON_ALL_IMPLS", + "emesonargs"]), + + ("meson-multilib", ["BUILD_DIR", "EMESON_BUILDTYPE", "EMESON_SOURCE", + "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", + "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EPYTHON", "MULTIBUILD_ID", + "MULTIBUILD_VARIANT", "MULTIBUILD_VARIANTS", "MULTILIB_ABI_FLAG", + "MULTILIB_CHOST_TOOLS", "MULTILIB_COMPAT", "MULTILIB_USEDEP", + "MULTILIB_WRAPPED_HEADERS", "MYMESONARGS", "NINJAOPTS", "PYTHON", + "PYTHON_COMPAT_NO_STRICT", "_MULTILIB_FLAGS", "_PYTHON_ALL_IMPLS", + "emesonargs"]), + + ("multibuild", ["BUILD_DIR", "MULTIBUILD_ID", "MULTIBUILD_VARIANT", + "MULTIBUILD_VARIANTS"]), + + ("multilib-build", ["BUILD_DIR", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", + "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", + "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", + "EPATCH_USER_SOURCE", "MULTIBUILD_ID", "MULTIBUILD_VARIANT", + "MULTIBUILD_VARIANTS", "MULTILIB_ABI_FLAG", "MULTILIB_CHOST_TOOLS", + "MULTILIB_COMPAT", "MULTILIB_USEDEP", "MULTILIB_WRAPPED_HEADERS", + "_MULTILIB_FLAGS"]), + + ("multilib-minimal", ["BUILD_DIR", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", + "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", + "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", + "EPATCH_USER_SOURCE", "MULTIBUILD_ID", "MULTIBUILD_VARIANT", + "MULTIBUILD_VARIANTS", "MULTILIB_ABI_FLAG", "MULTILIB_CHOST_TOOLS", + "MULTILIB_COMPAT", "MULTILIB_USEDEP", "MULTILIB_WRAPPED_HEADERS", + "_MULTILIB_FLAGS"]), + + ("ninja-utils", ["NINJAOPTS"]), + + ("obs-download", ["OBS_PACKAGE", "OBS_PROJECT", "OPENSUSE_RELEASE"]), + + ("obs-service", ["ADDITIONAL_FILES", "OBS_PACKAGE", "OBS_PROJECT", + "OBS_SERVICE_NAME", "OPENSUSE_RELEASE"]), + + ("osreleased", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("pam", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("perl-app", ["ALTERNATIVES", "DIST_A", "DIST_AUTHOR", "DIST_A_EXT", + "DIST_EXAMPLES", "DIST_NAME", "DIST_SECTION", "DIST_TEST", + "DIST_TEST_OVERRIDE", "DIST_VERSION", "EPATCH_COMMON_OPTS", + "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", + "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "GENTOO_DEPEND_ON_PERL", + "SOURCE", "UNPACKER_BZ2"]), + + ("perl-functions", ["ALTERNATIVES", "SOURCE"]), + + ("perl-module", ["ALTERNATIVES", "DIST_A", "DIST_AUTHOR", "DIST_A_EXT", + "DIST_EXAMPLES", "DIST_NAME", "DIST_SECTION", "DIST_TEST", + "DIST_TEST_OVERRIDE", "DIST_VERSION", "EPATCH_COMMON_OPTS", + "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", + "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "GENTOO_DEPEND_ON_PERL", + "SOURCE", "UNPACKER_BZ2"]), + + ("php-pear", ["PEAR_PV", "PHP_PEAR_PKG_NAME"]), + + ("php-pear-r1", ["PEAR_PV", "PHP_PEAR_PKG_NAME"]), + + ("platform", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", + "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", + "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", + "CROS_WORKON_ALWAYS_LIVE", "CROS_WORKON_COMMIT", "CROS_WORKON_DESTDIR", + "CROS_WORKON_EGIT_BRANCH", "CROS_WORKON_INCREMENTAL_BUILD", + "CROS_WORKON_INPLACE", "CROS_WORKON_LOCALNAME", + "CROS_WORKON_MAKE_COMPILE_ARGS", "CROS_WORKON_MANUAL_UPREV", + "CROS_WORKON_OPTIONAL_CHECKOUT", "CROS_WORKON_OUTOFTREE_BUILD", + "CROS_WORKON_PROJECT", "CROS_WORKON_REPO", "CROS_WORKON_SRCPATH", + "CROS_WORKON_SRCROOT", "CROS_WORKON_SUBDIRS_TO_COPY", + "CROS_WORKON_SUBDIRS_TO_REV", "CROS_WORKON_SUBTREE", "CROS_WORKON_TREE", + "CROS_WORKON_USE_VCSID", "EGIT_BOOTSTRAP", "EGIT_BRANCH", "EGIT_COMMIT", + "EGIT_DIR", "EGIT_HAS_SUBMODULES", "EGIT_MASTER", "EGIT_NONBARE", + "EGIT_NOUNPACK", "EGIT_OPTIONS", "EGIT_PROJECT", "EGIT_PRUNE", + "EGIT_REPACK", "EGIT_REPO_URI", "EGIT_SOURCEDIR", "EGIT_STORE_DIR", + "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", + "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EVCS_OFFLINE", "OUT", + "PLATFORM_ARC_BUILD", "PLATFORM_BUILD", "PLATFORM_NATIVE_TEST", + "PLATFORM_SUBDIR", "WANT_LIBBRILLO", "WANT_LIBCHROME"]), + + ("plocale", ["PLOCALES", "PLOCALE_BACKUP"]), + + ("prefix", ["EPREFIX"]), + + ("python-any-r1", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EPYTHON", + "PYTHON", "PYTHON_COMPAT", "PYTHON_COMPAT_NO_STRICT", + "PYTHON_COMPAT_OVERRIDE", "PYTHON_DEPS", "PYTHON_REQ_USE", "PYTHON_USEDEP", + "_PYTHON_ALL_IMPLS"]), + + ("python-r1", ["BUILD_DIR", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", + "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", + "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", + "EPATCH_USER_SOURCE", "EPYTHON", "MULTIBUILD_ID", "MULTIBUILD_VARIANT", + "MULTIBUILD_VARIANTS", "PYTHON", "PYTHON_COMPAT", "PYTHON_COMPAT_NO_STRICT", + "PYTHON_COMPAT_OVERRIDE", "PYTHON_DEPS", "PYTHON_REQUIRED_USE", + "PYTHON_REQ_USE", "PYTHON_USEDEP", "_PYTHON_ALL_IMPLS"]), + + ("python-single-r1", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", + "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", + "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", + "EPATCH_USER_SOURCE", "EPYTHON", "PYTHON", "PYTHON_COMPAT", + "PYTHON_COMPAT_NO_STRICT", "PYTHON_COMPAT_OVERRIDE", "PYTHON_DEPS", + "PYTHON_MULTI_USEDEP", "PYTHON_REQUIRED_USE", "PYTHON_REQ_USE", + "PYTHON_SINGLE_USEDEP", "PYTHON_USEDEP", "_PYTHON_ALL_IMPLS"]), + + ("python-utils-r1", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EPYTHON", + "PYTHON", "PYTHON_COMPAT_NO_STRICT", "_PYTHON_ALL_IMPLS"]), + + ("qmake-utils", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", + "EQMAKE4_EXCLUDE"]), + + ("readme.gentoo", ["DISABLE_AUTOFORMATTING", "EPATCH_COMMON_OPTS", + "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", + "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "FORCE_PRINT_ELOG", + "README_GENTOO_SUFFIX"]), + + ("readme.gentoo-r1", ["DISABLE_AUTOFORMATTING", "FORCE_PRINT_ELOG", + "README_GENTOO_SUFFIX"]), + + ("rpm", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), + + ("ruby", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "PATCHES"]), + + ("ruby-ng", ["RUBY_OPTIONAL", "RUBY_PATCHES", "RUBY_QA_ALLOWED_LIBS", + "RUBY_S", "USE_RUBY"]), + + ("ruby-single", ["RUBY_DEPS", "RUBY_TARGETS_PREFERENCE", "USE_RUBY"]), + + ("ruby-utils", ["RUBY_TARGETS_PREFERENCE"]), + + ("scons-utils", ["EXTRA_ESCONS", "SCONSOPTS", "SCONS_MIN_VERSION", + "USE_SCONS_FALSE", "USE_SCONS_TRUE", "myesconsargs"]), + + ("sgml-catalog", ["DOCS", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", + "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", + "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", + "EPATCH_USER_SOURCE", "HTML_DOCS", "PATCHES"]), + + ("tast-bundle", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", + "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", + "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", + "CROS_GO_BINARIES", "CROS_GO_PACKAGES", "CROS_GO_SKIP_DEP_CHECK", + "CROS_GO_SOURCE", "CROS_GO_TEST", "CROS_GO_VERSION", "CROS_GO_VET", + "CROS_GO_VET_FLAGS", "CROS_GO_WORKSPACE", "CROS_WORKON_ALWAYS_LIVE", + "CROS_WORKON_COMMIT", "CROS_WORKON_DESTDIR", "CROS_WORKON_EGIT_BRANCH", + "CROS_WORKON_INCREMENTAL_BUILD", "CROS_WORKON_INPLACE", + "CROS_WORKON_LOCALNAME", "CROS_WORKON_MAKE_COMPILE_ARGS", + "CROS_WORKON_MANUAL_UPREV", "CROS_WORKON_OPTIONAL_CHECKOUT", + "CROS_WORKON_OUTOFTREE_BUILD", "CROS_WORKON_PROJECT", "CROS_WORKON_REPO", + "CROS_WORKON_SRCPATH", "CROS_WORKON_SRCROOT", "CROS_WORKON_SUBDIRS_TO_COPY", + "CROS_WORKON_SUBDIRS_TO_REV", "CROS_WORKON_SUBTREE", "CROS_WORKON_TREE", + "CROS_WORKON_USE_VCSID", "EGIT_BOOTSTRAP", "EGIT_BRANCH", "EGIT_COMMIT", + "EGIT_DIR", "EGIT_HAS_SUBMODULES", "EGIT_MASTER", "EGIT_NONBARE", + "EGIT_NOUNPACK", "EGIT_OPTIONS", "EGIT_PROJECT", "EGIT_PRUNE", + "EGIT_REPACK", "EGIT_REPO_URI", "EGIT_SOURCEDIR", "EGIT_STORE_DIR", + "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", + "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EVCS_OFFLINE", + "TAST_BUNDLE_EXCLUDE_DATA_FILES", "TAST_BUNDLE_PRIVATE"]), + + ("tegra-bct", ["TEGRA_BCT_CHIP_FAMILY", "TEGRA_BCT_FLASH_CONFIG", + "TEGRA_BCT_ODM_DATA_CONFIG", "TEGRA_BCT_SDRAM_CONFIG"]), + + ("toolchain", ["EGIT_BOOTSTRAP", "EGIT_BRANCH", "EGIT_COMMIT", "EGIT_DIR", + "EGIT_HAS_SUBMODULES", "EGIT_MASTER", "EGIT_NONBARE", "EGIT_NOUNPACK", + "EGIT_OPTIONS", "EGIT_PROJECT", "EGIT_PRUNE", "EGIT_REPACK", + "EGIT_REPO_URI", "EGIT_SOURCEDIR", "EGIT_STORE_DIR", "EPATCH_COMMON_OPTS", + "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", + "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", + "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EVCS_OFFLINE"]), + + ("unpacker", ["UNPACKER_BZ2"]), + + ("vala", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", + "VALA_MAX_API_VERSION", "VALA_MIN_API_VERSION", "VALA_USE_DEPEND"]), + + ("vdr-plugin", ["DOCS", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", + "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", + "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", + "EPATCH_USER_SOURCE", "HTML_DOCS", "PATCHES"]), + + ("verify-sig", ["VERIFY_SIG_OPENPGP_KEYSERVER", + "VERIFY_SIG_OPENPGP_KEY_PATH", "VERIFY_SIG_OPENPGP_KEY_REFRESH"]), + + ("waf-utils", ["WAF_BINARY", "WAF_VERBOSE"]), + + ("webapp", ["WEBAPP_DEPEND", "WEBAPP_NO_AUTO_INSTALL", "WEBAPP_OPTIONAL"]), + + ("wxwidgets", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", + "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", + "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "WX_GTK_VER"]), + + ("xdg", ["DESKTOP_DATABASE_DIR", "MIMEINFO_DATABASE_DIR"]), + + ("xdg-utils", ["DESKTOP_DATABASE_DIR", "MIMEINFO_DATABASE_DIR"]), + + ("xemacs-elisp", ["SIMPLE_ELISP"]), + + ("xorg-2", ["AM_OPTS", "AT_M4DIR", "AT_NOEAUTOMAKE", "AT_NOELIBTOOLIZE", + "AT_SYS_M4DIR", "AUTOTOOLS_AUTORECONF", "AUTOTOOLS_AUTO_DEPEND", + "AUTOTOOLS_IN_SOURCE_BUILD", "AUTOTOOLS_PRUNE_LIBTOOL_FILES", "BUILD_DIR", + "DOCS", "ECONF_SOURCE", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", + "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", + "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", + "EPATCH_USER_SOURCE", "FONT_DIR", "HTML_DOCS", "MULTIBUILD_ID", + "MULTIBUILD_VARIANT", "MULTIBUILD_VARIANTS", "MULTILIB_ABI_FLAG", + "MULTILIB_CHOST_TOOLS", "MULTILIB_COMPAT", "MULTILIB_USEDEP", + "MULTILIB_WRAPPED_HEADERS", "PATCHES", "WANT_AUTOCONF", "WANT_AUTOMAKE", + "WANT_LIBTOOL", "XORG_BASE_INDIVIDUAL_URI", "XORG_CONFIGURE_OPTIONS", + "XORG_DOC", "XORG_DRI", "XORG_EAUTORECONF", "XORG_MODULE", + "XORG_MODULE_REBUILD", "XORG_MULTILIB", "XORG_PACKAGE_NAME", "XORG_STATIC", + "_LATEST_AUTOMAKE", "_MULTILIB_FLAGS", "myeconfargs"]), + + ("xorg-3", ["BUILD_DIR", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", + "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", + "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", + "EPATCH_USER_SOURCE", "FONT_DIR", "MULTIBUILD_ID", "MULTIBUILD_VARIANT", + "MULTIBUILD_VARIANTS", "MULTILIB_ABI_FLAG", "MULTILIB_CHOST_TOOLS", + "MULTILIB_COMPAT", "MULTILIB_USEDEP", "MULTILIB_WRAPPED_HEADERS", + "XORG_BASE_INDIVIDUAL_URI", "XORG_CONFIGURE_OPTIONS", "XORG_DOC", + "XORG_DRI", "XORG_EAUTORECONF", "XORG_MODULE", "XORG_MULTILIB", + "XORG_PACKAGE_NAME", "XORG_STATIC", "XORG_TARBALL_SUFFIX", + "_MULTILIB_FLAGS"]) + ] From 08ae7ef83621ece82d6644b95d95abdde62ee696 Mon Sep 17 00:00:00 2001 From: hololeap Date: Fri, 4 Aug 2023 17:19:05 -0600 Subject: [PATCH 07/10] New IO interface to scan for Gentoo eclass vars Uses the `portageq` command to scan for repositories, which in turn are scanned for eclasses, which are then scanned for eclass variables. The variables are scanned using a heuristic which looks for "# @ECLASS_VARIABLE: " at the start of each line, which means only properly documented variables will be found. Signed-off-by: hololeap --- ShellCheck.cabal | 1 + src/ShellCheck/PortageVariables.hs | 158 +++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 src/ShellCheck/PortageVariables.hs diff --git a/ShellCheck.cabal b/ShellCheck.cabal index f9c8c76..45408a3 100644 --- a/ShellCheck.cabal +++ b/ShellCheck.cabal @@ -97,6 +97,7 @@ library ShellCheck.Regex other-modules: Paths_ShellCheck + ShellCheck.PortageVariables ShellCheck.PortageAutoInternalVariables default-language: Haskell98 diff --git a/src/ShellCheck/PortageVariables.hs b/src/ShellCheck/PortageVariables.hs new file mode 100644 index 0000000..ab03cda --- /dev/null +++ b/src/ShellCheck/PortageVariables.hs @@ -0,0 +1,158 @@ + +module ShellCheck.PortageVariables + ( RepoName + , RepoPath + , EclassVar + , Repository(..) + , Eclass(..) + , portageVariables + , scanRepos + ) where + +import Control.Applicative +import Control.Monad +import Control.Monad.Trans.Class +import Control.Monad.Trans.Maybe +import Data.Map (Map) +import qualified Data.Map as M +import System.Directory (listDirectory) +import System.Exit (ExitCode(..)) +import System.FilePath +import System.Process -- (readProcessWithExitCode) +import Text.Parsec hiding ((<|>)) +import Text.Parsec.String + +type RepoName = String +type RepoPath = FilePath +type EclassVar = String + +data Repository = Repository + { repositoryName :: RepoName + , repositoryLocation :: RepoPath + , repositoryEclasses :: [Eclass] + } deriving (Show, Eq, Ord) + +data Eclass = Eclass + { eclassName :: String + , eclassVars :: [EclassVar] + } deriving (Show, Eq, Ord) + +portageVariables :: [Repository] -> Map String [EclassVar] +portageVariables = foldMap $ foldMap go . repositoryEclasses + where + go e = M.singleton (eclassName e) (eclassVars e) + +-- | Run @portageq@ to gather a list of repo names and paths, then scan each +-- one for eclasses and ultimately eclass metadata. +scanRepos :: IO [Repository] +scanRepos = do + let cmd = "/usr/bin/portageq" + let args = ["repos_config", "/"] + out <- runOrDie cmd args + case parse reposParser "scanRepos" out of + Left pe -> fail $ show pe + Right nps -> do + forM nps $ \(n,p) -> Repository n p <$> getEclasses p + +-- | Get the name of the repo and its path from blocks outputted by +-- @portageq@. If the path doesn't exist, this will return @Nothing@. +reposParser :: Parser [(RepoName, RepoPath)] +reposParser = + choice + [ [] <$ eof + , repoName >>= repoBlock + ] + where + -- Get the name of the repo at the top of the block + repoName :: Parser RepoName + repoName + = char '[' + *> manyTill anyChar (try (char ']')) + <* endOfLine + + -- Parse the block for location field + repoBlock :: RepoName -> Parser [(RepoName, RepoPath)] + repoBlock n = choice + [ try $ do + l <- string "location = " *> takeLine + -- Found the location, skip the rest of the block + skipMany miscLine *> endOfBlock + insert (n,l) + -- Did not find the location, keep trying + , try $ miscLine *> repoBlock n + -- Reached the end of the block, no location field + , endOfBlock *> ignore + ] + + miscLine :: Parser () + miscLine = skipNonEmptyLine + + -- A block ends with an eol or eof + endOfBlock :: Parser () + endOfBlock = void endOfLine <|> eof + + insert :: (RepoName, RepoPath) -> Parser [(RepoName, RepoPath)] + insert r = (r:) <$> reposParser + + ignore :: Parser [(RepoName, RepoPath)] + ignore = reposParser + +-- | Scan the repo path for @*.eclass@ files in @eclass/@, then run +-- 'eclassParser' on each of them to produce @[Eclass]@. +-- +-- If the @eclass/@ directory doesn't exist, the scan is skipped for that +-- repo. +getEclasses :: RepoPath -> IO [Eclass] +getEclasses repoLoc = fmap (maybe [] id) $ runMaybeT $ do + let eclassDir = repoLoc "eclass" + + -- Silently fail if the repo doesn't have an eclass dir + fs <- MaybeT $ Just <$> listDirectory eclassDir <|> pure Nothing + let fs' = filter (\(_,e) -> e == ".eclass") $ map splitExtensions fs + + forM fs' $ \(n,e) -> do + evs <- lift $ parseFromFile eclassParser (eclassDir n <.> e) + case evs of + Left pe -> lift $ fail $ show pe + Right vs -> pure $ Eclass n vs + +eclassParser :: Parser [EclassVar] +eclassParser = choice + [ -- cons the EclassVar to the list and continue + try $ liftA2 (:) eclassVar eclassParser + -- or skip the line and continue + , skipLine *> eclassParser + -- or end the list on eof + , [] <$ eof + ] + where + -- Scans for @ECLASS_VARIABLE comments rather than parsing the raw bash + eclassVar :: Parser EclassVar + eclassVar = string "# @ECLASS_VARIABLE: " *> takeLine + +takeLine :: Parser String +takeLine = manyTill anyChar (try endOfLine) + +-- | Fails if next char is 'endOfLine' +skipNonEmptyLine :: Parser () +skipNonEmptyLine = notFollowedBy endOfLine *> skipLine + +skipLine :: Parser () +skipLine = void takeLine + +-- | Run the command and return the full stdout string (stdin is ignored). +-- +-- If the command exits with a non-zero exit code, this will throw an +-- error including the captured contents of stdout and stderr. +runOrDie :: FilePath -> [String] -> IO String +runOrDie cmd args = do + (ec, o, e) <- readProcessWithExitCode cmd args "" + case ec of + ExitSuccess -> pure o + ExitFailure i -> fail $ unlines $ map unwords + $ [ [ show cmd ] + ++ map show args + ++ [ "failed with exit code", show i] + , [ "stdout:" ], [ o ] + , [ "stderr:" ], [ e ] + ] From 272ef819b91e591aacc9adfccf0c0f1707905b35 Mon Sep 17 00:00:00 2001 From: hololeap Date: Sat, 5 Aug 2023 15:16:30 -0600 Subject: [PATCH 08/10] Scan for Gentoo eclass variables Creates a Map of eclass names to eclass variables by scanning the system for repositories and their respective eclasses. Runs `portageq` to determine repository names and locations. Emits a warning if an IOException is caught when attempting to run `portageq`. This Map is passed via CheckSpec to AnalysisSpec and finally to Parameters, where it is read by `checkUnusedAssignments` in order to determine which variables can be safely ignored by this check. Signed-off-by: hololeap --- ShellCheck.cabal | 2 +- shellcheck.hs | 15 ++++++++++++++- src/ShellCheck/Analytics.hs | 5 ++++- src/ShellCheck/AnalyzerLib.hs | 4 ++++ src/ShellCheck/Checker.hs | 3 ++- src/ShellCheck/Data.hs | 12 +++++++----- src/ShellCheck/Interface.hs | 17 +++++++++++------ src/ShellCheck/PortageVariables.hs | 18 +++++++++++++++--- 8 files changed, 58 insertions(+), 18 deletions(-) diff --git a/ShellCheck.cabal b/ShellCheck.cabal index 45408a3..6f6a7a3 100644 --- a/ShellCheck.cabal +++ b/ShellCheck.cabal @@ -93,11 +93,11 @@ library ShellCheck.Formatter.Quiet ShellCheck.Interface ShellCheck.Parser + ShellCheck.PortageVariables ShellCheck.Prelude ShellCheck.Regex other-modules: Paths_ShellCheck - ShellCheck.PortageVariables ShellCheck.PortageAutoInternalVariables default-language: Haskell98 diff --git a/shellcheck.hs b/shellcheck.hs index 4e8a155..d66e61c 100644 --- a/shellcheck.hs +++ b/shellcheck.hs @@ -21,6 +21,7 @@ import qualified ShellCheck.Analyzer import ShellCheck.Checker import ShellCheck.Data import ShellCheck.Interface +import ShellCheck.PortageVariables import ShellCheck.Regex import qualified ShellCheck.Formatter.CheckStyle @@ -240,10 +241,22 @@ runFormatter sys format options files = do either (reportFailure filename) check input where check contents = do + + -- If this is a Gentoo ebuild file, scan for eclasses on the system + gentooData <- case getPortageFileType filename of + NonPortageRelated -> pure Map.empty + _ -> catch (portageVariables <$> scanRepos) $ \e -> do + let warnMsg = "Error when scanning for Gentoo repos: " + let err = show (e :: IOException) + hPutStr stderr ("Warning: " ++ warnMsg ++ err) + pure Map.empty + let checkspec = (checkSpec options) { csFilename = filename, - csScript = contents + csScript = contents, + csGentooData = gentooData } + result <- checkScript sys checkspec onResult format result sys return $ diff --git a/src/ShellCheck/Analytics.hs b/src/ShellCheck/Analytics.hs index ad7f24c..57c6fa4 100644 --- a/src/ShellCheck/Analytics.hs +++ b/src/ShellCheck/Analytics.hs @@ -2412,7 +2412,10 @@ allInternalVariables params = genericInternalVariables ++ if shellType params == Ksh then kshInternalVariables else [] ++ if isPortageBuild params - then portageInternalVariables (getInheritedEclasses (rootNode params)) + then + let eclasses = getInheritedEclasses $ rootNode params + gMap = gentooData params + in portageInternalVariables eclasses gMap else [] prop_checkUnused0 = verifyNotTree checkUnusedAssignments "var=foo; echo $var" diff --git a/src/ShellCheck/AnalyzerLib.hs b/src/ShellCheck/AnalyzerLib.hs index 8105b65..9ffb20f 100644 --- a/src/ShellCheck/AnalyzerLib.hs +++ b/src/ShellCheck/AnalyzerLib.hs @@ -27,6 +27,7 @@ import qualified ShellCheck.CFGAnalysis as CF import ShellCheck.Data import ShellCheck.Interface import ShellCheck.Parser +import ShellCheck.PortageVariables import ShellCheck.Prelude import ShellCheck.Regex @@ -104,6 +105,8 @@ data Parameters = Parameters { tokenPositions :: Map.Map Id (Position, Position), -- detailed type of any Portage related file portageFileType :: PortageFileType, + -- Gentoo-specific data + gentooData :: EclassMap, -- Result from Control Flow Graph analysis (including data flow analysis) cfgAnalysis :: CF.CFGAnalysis } deriving (Show) @@ -243,6 +246,7 @@ makeParameters spec = params variableFlow = getVariableFlow params root, tokenPositions = asTokenPositions spec, portageFileType = asPortageFileType spec, + gentooData = asGentooData spec, cfgAnalysis = CF.analyzeControlFlow cfParams root } cfParams = CF.CFGParameters { diff --git a/src/ShellCheck/Checker.hs b/src/ShellCheck/Checker.hs index 5cd6ae8..2bbc357 100644 --- a/src/ShellCheck/Checker.hs +++ b/src/ShellCheck/Checker.hs @@ -89,7 +89,8 @@ checkScript sys spec = do asExecutionMode = Executed, asTokenPositions = tokenPositions, asOptionalChecks = getEnableDirectives root ++ csOptionalChecks spec, - asPortageFileType = getPortageFileType $ csFilename spec + asPortageFileType = getPortageFileType $ csFilename spec, + asGentooData = csGentooData spec } where as = newAnalysisSpec root let analysisMessages = maybe [] diff --git a/src/ShellCheck/Data.hs b/src/ShellCheck/Data.hs index 6a7ab6c..3887912 100644 --- a/src/ShellCheck/Data.hs +++ b/src/ShellCheck/Data.hs @@ -2,6 +2,7 @@ module ShellCheck.Data where import qualified Data.Map import ShellCheck.Interface +import ShellCheck.PortageVariables import ShellCheck.PortageAutoInternalVariables import Data.Version (showVersion) @@ -149,14 +150,15 @@ portageManualInternalVariables = [ "LINGUAS" ] -eclassVarsFromMap :: String -> [String] -eclassVarsFromMap eclass = +eclassVarsFromMap :: EclassMap -> String -> [String] +eclassVarsFromMap gMap eclass = Data.Map.findWithDefault [] eclass - portageAutoInternalVariables + gMap -portageInternalVariables inheritedEclasses = - portageManualInternalVariables ++ concatMap eclassVarsFromMap +portageInternalVariables :: [String] -> EclassMap -> [String] +portageInternalVariables inheritedEclasses gMap = + portageManualInternalVariables ++ concatMap (eclassVarsFromMap gMap) inheritedEclasses specialIntegerVariables = [ diff --git a/src/ShellCheck/Interface.hs b/src/ShellCheck/Interface.hs index 60a9b94..726fb3b 100644 --- a/src/ShellCheck/Interface.hs +++ b/src/ShellCheck/Interface.hs @@ -21,11 +21,11 @@ module ShellCheck.Interface ( SystemInterface(..) - , CheckSpec(csFilename, csScript, csCheckSourced, csIncludedWarnings, csExcludedWarnings, csShellTypeOverride, csMinSeverity, csIgnoreRC, csOptionalChecks) + , CheckSpec(csFilename, csScript, csCheckSourced, csIncludedWarnings, csExcludedWarnings, csShellTypeOverride, csMinSeverity, csIgnoreRC, csOptionalChecks, csGentooData) , CheckResult(crFilename, crComments) , ParseSpec(psFilename, psScript, psCheckSourced, psIgnoreRC, psShellTypeOverride) , ParseResult(prComments, prTokenPositions, prRoot) - , AnalysisSpec(asScript, asShellType, asFallbackShell, asExecutionMode, asCheckSourced, asTokenPositions, asOptionalChecks, asPortageFileType) + , AnalysisSpec(asScript, asShellType, asFallbackShell, asExecutionMode, asCheckSourced, asTokenPositions, asOptionalChecks, asPortageFileType, asGentooData) , AnalysisResult(arComments) , FormatterOptions(foColorOption, foWikiLinkCount) , Shell(Ksh, Sh, Bash, Dash) @@ -63,6 +63,7 @@ module ShellCheck.Interface ) where import ShellCheck.AST +import ShellCheck.PortageVariables (EclassMap) import Control.DeepSeq import Control.Monad.Identity @@ -101,7 +102,8 @@ data CheckSpec = CheckSpec { csIncludedWarnings :: Maybe [Integer], csShellTypeOverride :: Maybe Shell, csMinSeverity :: Severity, - csOptionalChecks :: [String] + csOptionalChecks :: [String], + csGentooData :: EclassMap } deriving (Show, Eq) data CheckResult = CheckResult { @@ -125,7 +127,8 @@ emptyCheckSpec = CheckSpec { csIncludedWarnings = Nothing, csShellTypeOverride = Nothing, csMinSeverity = StyleC, - csOptionalChecks = [] + csOptionalChecks = [], + csGentooData = Map.empty } newParseSpec :: ParseSpec @@ -182,7 +185,8 @@ data AnalysisSpec = AnalysisSpec { asCheckSourced :: Bool, asOptionalChecks :: [String], asTokenPositions :: Map.Map Id (Position, Position), - asPortageFileType :: PortageFileType + asPortageFileType :: PortageFileType, + asGentooData :: EclassMap } newAnalysisSpec token = AnalysisSpec { @@ -193,7 +197,8 @@ newAnalysisSpec token = AnalysisSpec { asCheckSourced = False, asOptionalChecks = [], asTokenPositions = Map.empty, - asPortageFileType = NonPortageRelated + asPortageFileType = NonPortageRelated, + asGentooData = Map.empty } newtype AnalysisResult = AnalysisResult { diff --git a/src/ShellCheck/PortageVariables.hs b/src/ShellCheck/PortageVariables.hs index ab03cda..4e79b8a 100644 --- a/src/ShellCheck/PortageVariables.hs +++ b/src/ShellCheck/PortageVariables.hs @@ -2,7 +2,9 @@ module ShellCheck.PortageVariables ( RepoName , RepoPath + , EclassName , EclassVar + , EclassMap , Repository(..) , Eclass(..) , portageVariables @@ -18,14 +20,19 @@ import qualified Data.Map as M import System.Directory (listDirectory) import System.Exit (ExitCode(..)) import System.FilePath -import System.Process -- (readProcessWithExitCode) +import System.Process import Text.Parsec hiding ((<|>)) import Text.Parsec.String type RepoName = String type RepoPath = FilePath +type EclassName = String type EclassVar = String +-- | This is used for looking up what eclass variables are inherited, +-- keyed by the name of the eclass. +type EclassMap = M.Map EclassName [EclassVar] + data Repository = Repository { repositoryName :: RepoName , repositoryLocation :: RepoPath @@ -33,11 +40,12 @@ data Repository = Repository } deriving (Show, Eq, Ord) data Eclass = Eclass - { eclassName :: String + { eclassName :: EclassName , eclassVars :: [EclassVar] } deriving (Show, Eq, Ord) -portageVariables :: [Repository] -> Map String [EclassVar] +-- | Map from eclass names to a list of eclass variables +portageVariables :: [Repository] -> EclassMap portageVariables = foldMap $ foldMap go . repositoryEclasses where go e = M.singleton (eclassName e) (eclassVars e) @@ -91,9 +99,11 @@ reposParser = endOfBlock :: Parser () endOfBlock = void endOfLine <|> eof + -- cons the repo and continue parsing insert :: (RepoName, RepoPath) -> Parser [(RepoName, RepoPath)] insert r = (r:) <$> reposParser + -- skip the repo and continue parsing ignore :: Parser [(RepoName, RepoPath)] ignore = reposParser @@ -116,6 +126,8 @@ getEclasses repoLoc = fmap (maybe [] id) $ runMaybeT $ do Left pe -> lift $ fail $ show pe Right vs -> pure $ Eclass n vs +-- | Scan a @.eclass@ file for any @@@ECLASS_VARIABLE:@ comments, generating +-- a list of eclass variables. eclassParser :: Parser [EclassVar] eclassParser = choice [ -- cons the EclassVar to the list and continue From fc9b63fb5eea29de9300a7300f41ba668a3089b1 Mon Sep 17 00:00:00 2001 From: hololeap Date: Sat, 5 Aug 2023 15:19:35 -0600 Subject: [PATCH 09/10] Remove PortageAutoInternalVariables and python The Gentoo eclass list is now populated using pure Haskell. The old python generators and generated module are no longer needed. Signed-off-by: hololeap --- ShellCheck.cabal | 1 - portage/get_vars.py | 128 --- portage/get_vars_diff.py | 120 --- src/ShellCheck/Data.hs | 1 - .../PortageAutoInternalVariables.hs | 823 ------------------ 5 files changed, 1073 deletions(-) delete mode 100644 portage/get_vars.py delete mode 100644 portage/get_vars_diff.py delete mode 100644 src/ShellCheck/PortageAutoInternalVariables.hs diff --git a/ShellCheck.cabal b/ShellCheck.cabal index 6f6a7a3..3166777 100644 --- a/ShellCheck.cabal +++ b/ShellCheck.cabal @@ -98,7 +98,6 @@ library ShellCheck.Regex other-modules: Paths_ShellCheck - ShellCheck.PortageAutoInternalVariables default-language: Haskell98 executable shellcheck diff --git a/portage/get_vars.py b/portage/get_vars.py deleted file mode 100644 index 58258c9..0000000 --- a/portage/get_vars.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# Copyright 2019 The ChromiumOS Authors -# All rights reserved. - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: - -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google LLC nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# Binary License Terms - -"""Extract eclass variable names into Haskell list format.""" -from __future__ import print_function -import datetime -import os -import re -import sys -import textwrap -# Matches a line that declares a variable in an eclass. -VAR_RE = re.compile(r'@(?:ECLASS-)?VARIABLE:\s*(\w+)$') -# Matches a line that declares inheritance. -INHERIT_RE = re.compile(r'^[^#]*\binherit((?:\s+[\w-]+)+)$') -VAR_FILE_HEADER = """module ShellCheck.PortageAutoInternalVariables ( - portageAutoInternalVariables - ) where --- This file contains the variables generated by --- portage/get_vars.py""" -PORTAGE_AUTO_VAR_NAME = 'portageAutoInternalVariables' -class Eclass: - """Container for eclass information""" - def __init__(self, name, eclass_vars, inheritances): - self.name = name - self.vars = eclass_vars - self.inheritances = inheritances - def calculate_eclass_vars(self, eclasses): - while self.inheritances: - name = self.inheritances.pop() - try: - sub_eclass = eclasses[name] - new_vars = sub_eclass.calculate_eclass_vars(eclasses).vars - self.vars = self.vars.union(new_vars) - except Exception: - pass - return self -def print_var_list(eclass, eclass_vars): - var_list = ' '.join(['"%s",' % v for v in sorted(eclass_vars)]) - print(' -- %s\n%s' % - (eclass, - textwrap.fill( - var_list, 80, initial_indent=' ', subsequent_indent=' '))) -def process_file(eclass_path): - eclass_name = os.path.splitext(os.path.basename(eclass_path))[0] - with open(eclass_path, 'r') as f: - eclass_vars = set() - eclass_inheritances = set() - for line in f: - line = line.strip() - if not line: - continue - while line[-1] == '\\': - line = line[:-1] + next(f).strip() - match = VAR_RE.search(line) - if match: - var_name = match.group(1) - eclass_vars.add(var_name.strip()) - else: - match = INHERIT_RE.search(line) - if match: - for inheritance in re.split(r'\s+', match.group(1)): - if inheritance.strip(): - eclass_inheritances.add(inheritance.strip()) - return Eclass(eclass_name, eclass_vars, eclass_inheritances) -def format_eclasses_as_haskell_map(eclasses): - map_entries = [] - join_string = '", "' - for value in sorted(eclasses, key=(lambda x: x.name)): - if value.vars: - var_list_string = f'"{join_string.join(sorted(list(value.vars)))}"' - map_entries.append( - textwrap.fill( - f'("{value.name}", [{var_list_string}])', - 80, - initial_indent=' ', - subsequent_indent=' ')) - return_string = ',\n\n'.join(map_entries) - return_string = f""" Data.Map.fromList - [ -{return_string} - ]""" - return f"""{VAR_FILE_HEADER}\n\n --- Last Generated: {datetime.datetime.now().strftime("%x")} -import qualified Data.Map -{PORTAGE_AUTO_VAR_NAME} = -{return_string}""" -def main(argv): - eclasses = {} - for path in sorted(argv, key=os.path.basename): - if not path.endswith('.eclass'): - continue - new_eclass = process_file(path) - eclasses[new_eclass.name] = new_eclass - eclasses_list = [ - value.calculate_eclass_vars(eclasses) for key, value in eclasses.items() - ] - print(format_eclasses_as_haskell_map(eclasses_list)) -if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) diff --git a/portage/get_vars_diff.py b/portage/get_vars_diff.py deleted file mode 100644 index 7351698..0000000 --- a/portage/get_vars_diff.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2020 The ChromiumOS Authors -# All rights reserved. - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: - -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google LLC nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# Binary License Terms - -"""Generates diff of vars from get_vars.py and those existing in Data.hs.""" -import itertools -from pathlib import Path -import subprocess -SCRIPT = Path(__file__).resolve() -THIRD_PARTY = SCRIPT.parent.parent.parent.parent.parent -# List of relative directories in which to find the eclasses. -eclass_rel_dirs = ( - THIRD_PARTY / 'chromiumos-overlay' / 'eclass', - THIRD_PARTY / 'portage-stable' / 'eclass', - THIRD_PARTY / 'eclass-overlay' / 'eclass', -) -# Runs get_vars.py with the eclass paths and store the output. -cmd = [SCRIPT.with_name('get_vars.py')] + list( - itertools.chain(*(x.glob('*') for x in eclass_rel_dirs))) -new_output = subprocess.check_output(cmd, encoding='utf-8').splitlines() -new = [] -for line in new_output: - if '--' in line: - new.append(line.strip()) - elif not line.strip(): - continue - else: - new += (line.replace('"', '').replace('\n', '').split(',')) -# Reads the Data.hs relevant area and store the lines. -data_hs = THIRD_PARTY / 'shellcheck' / 'src' / 'ShellCheck' / 'Data.hs' -with data_hs.open('r', encoding='utf-8') as fp: - record = False - old = [] - for line in fp: - if line.strip() == '-- autotest.eclass declared incorrectly': - break - if line.strip() == '-- generic ebuilds': - record = True - if record: - if '--' in line: - old.append(line.strip()) - elif not line.strip(): - continue - else: - old += line.replace('"', '').replace('\n', '').split(',') -# Cleans up empty bits as a result of parsing difficulties. -new = [x.strip() for x in new if x.strip()] -old = [x.strip() for x in old if x.strip()] -all_eclasses = set() -old_vars = {} -new_vars = {} -current_eclass = '' -for item in old: - if '--' in item: - # It's an eclass comment line. - current_eclass = item[3:] - all_eclasses.add(current_eclass) - continue - else: - # It's a var, so add it to the dict of the current eclass. - old_vars.setdefault(current_eclass, []).append(item) -for item in new: - if '--' in item: - # It's an eclass comment line. - current_eclass = item[3:] - all_eclasses.add(current_eclass) - continue - else: - # It's a var, so add it to the dict of the current eclass. - new_vars.setdefault(current_eclass, []).append(item) -for eclass in sorted(all_eclasses): - if eclass in old_vars: - if eclass not in new_vars: - # Checks if the entire eclass is removed. - print(f'{eclass} not present in new variables.') - for var in old_vars[eclass]: - print(f'\t-{var}') - print() - else: - # Eclass isn't removed, so check for added or removed vars. - toprint = '\n'.join( - [f'\t-{x}' for x in old_vars[eclass] if x not in new_vars[eclass]] + - [f'\t+{x}' for x in new_vars[eclass] if x not in old_vars[eclass]]) - if toprint: - print(eclass) - print(toprint) - if eclass in new_vars: - if eclass not in old_vars: - # Checks if entire eclass is new. - print(f'{eclass} added in new variables.') - for var in new_vars[eclass]: - print(f'\t+{var}') - print() diff --git a/src/ShellCheck/Data.hs b/src/ShellCheck/Data.hs index 3887912..299cbd6 100644 --- a/src/ShellCheck/Data.hs +++ b/src/ShellCheck/Data.hs @@ -3,7 +3,6 @@ module ShellCheck.Data where import qualified Data.Map import ShellCheck.Interface import ShellCheck.PortageVariables -import ShellCheck.PortageAutoInternalVariables import Data.Version (showVersion) diff --git a/src/ShellCheck/PortageAutoInternalVariables.hs b/src/ShellCheck/PortageAutoInternalVariables.hs deleted file mode 100644 index e5558e7..0000000 --- a/src/ShellCheck/PortageAutoInternalVariables.hs +++ /dev/null @@ -1,823 +0,0 @@ -module ShellCheck.PortageAutoInternalVariables ( - portageAutoInternalVariables - ) where - --- This file contains the variables generated by --- portage/get_vars.py - - --- Last Generated: 12/02/22 - -import qualified Data.Map - -portageAutoInternalVariables = - Data.Map.fromList - [ - ("alternatives", ["ALTERNATIVES", "SOURCE"]), - - ("apache-2", ["AM_OPTS", "AT_M4DIR", "AT_NOEAUTOMAKE", "AT_NOELIBTOOLIZE", - "AT_SYS_M4DIR", "AUTOTOOLS_AUTO_DEPEND", "EPATCH_COMMON_OPTS", - "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", - "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "GENTOO_DEVELOPER", - "GENTOO_PATCHDIR", "GENTOO_PATCHNAME", "GENTOO_PATCHSTAMP", - "GENTOO_PATCH_A", "IUSE_MODULES", "IUSE_MPMS_FORK", "IUSE_MPMS_THREAD", - "MODULE_CRITICAL", "MODULE_DEFINES", "MODULE_DEPENDS", "MY_CONF", "MY_MODS", - "MY_MPM", "WANT_AUTOCONF", "WANT_AUTOMAKE", "WANT_LIBTOOL", - "_LATEST_AUTOMAKE"]), - - ("arc-build", ["ARC_BASE", "ARC_ETC_DIR", "ARC_LLVM_VERSION", "ARC_PREFIX", - "ARC_VENDOR_DIR", "ARC_VERSION_MAJOR", "ARC_VERSION_MINOR", - "ARC_VERSION_PATCH", "AUTOTEST_BASE", "BUILD_DIR", "CHROMITE_BIN_DIR", - "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", "CROS_GIT_AOSP_URL", - "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", "EPATCH_COMMON_OPTS", - "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", - "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "MULTIBUILD_ID", - "MULTIBUILD_VARIANT", "MULTIBUILD_VARIANTS", "MULTILIB_ABI_FLAG", - "MULTILIB_CHOST_TOOLS", "MULTILIB_COMPAT", "MULTILIB_USEDEP", - "MULTILIB_WRAPPED_HEADERS", "_MULTILIB_FLAGS"]), - - ("autotest", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", - "AUTOTEST_BASE", "AUTOTEST_FILE_MASK", "AUTOTEST_FORCE_LIST", - "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", - "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL"]), - - ("autotest-deponly", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", - "AUTOTEST_BASE", "AUTOTEST_FILE_MASK", "AUTOTEST_FORCE_LIST", - "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", - "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL"]), - - ("autotest-external-dep", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", - "AUTOTEST_BASE", "AUTOTEST_FILE_MASK", "AUTOTEST_FORCE_LIST", - "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", - "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", - "PACKAGE"]), - - ("autotools", ["AM_OPTS", "AT_M4DIR", "AT_NOEAUTOMAKE", "AT_NOELIBTOOLIZE", - "AT_SYS_M4DIR", "AUTOTOOLS_AUTO_DEPEND", "WANT_AUTOCONF", "WANT_AUTOMAKE", - "WANT_LIBTOOL", "_LATEST_AUTOMAKE"]), - - ("autotools-multilib", ["AM_OPTS", "AT_M4DIR", "AT_NOEAUTOMAKE", - "AT_NOELIBTOOLIZE", "AT_SYS_M4DIR", "AUTOTOOLS_AUTORECONF", - "AUTOTOOLS_AUTO_DEPEND", "AUTOTOOLS_IN_SOURCE_BUILD", - "AUTOTOOLS_PRUNE_LIBTOOL_FILES", "BUILD_DIR", "DOCS", "ECONF_SOURCE", - "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", - "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "HTML_DOCS", "MULTIBUILD_ID", - "MULTIBUILD_VARIANT", "MULTIBUILD_VARIANTS", "MULTILIB_ABI_FLAG", - "MULTILIB_CHOST_TOOLS", "MULTILIB_COMPAT", "MULTILIB_USEDEP", - "MULTILIB_WRAPPED_HEADERS", "PATCHES", "WANT_AUTOCONF", "WANT_AUTOMAKE", - "WANT_LIBTOOL", "_LATEST_AUTOMAKE", "_MULTILIB_FLAGS", "myeconfargs"]), - - ("autotools-utils", ["AM_OPTS", "AT_M4DIR", "AT_NOEAUTOMAKE", - "AT_NOELIBTOOLIZE", "AT_SYS_M4DIR", "AUTOTOOLS_AUTORECONF", - "AUTOTOOLS_AUTO_DEPEND", "AUTOTOOLS_IN_SOURCE_BUILD", - "AUTOTOOLS_PRUNE_LIBTOOL_FILES", "BUILD_DIR", "DOCS", "ECONF_SOURCE", - "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", - "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "HTML_DOCS", "PATCHES", - "WANT_AUTOCONF", "WANT_AUTOMAKE", "WANT_LIBTOOL", "_LATEST_AUTOMAKE", - "myeconfargs"]), - - ("base", ["DOCS", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "HTML_DOCS", - "PATCHES"]), - - ("bash-completion", ["BASHCOMPFILES", "BASHCOMPLETION_NAME", - "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", - "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("cargo", ["CARGO_INSTALL_PATH"]), - - ("cbi-image", ["CROS_CBI_IMAGE_DIR", "EEPROM_SIZE"]), - - ("chromium-source", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", - "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", - "CHROMIUM_GCLIENT_TEMPLATE", "CHROOT_SOURCE_ROOT", "CROS_GIT_AOSP_URL", - "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", "DEPOT_TOOLS", "EGCLIENT", - "ENINJA"]), - - ("cmake", ["BUILD_DIR", "CMAKE_BINARY", "CMAKE_BUILD_TYPE", - "CMAKE_EXTRA_CACHE_FILE", "CMAKE_IN_SOURCE_BUILD", - "CMAKE_MAKEFILE_GENERATOR", "CMAKE_QA_SRC_DIR_READONLY", - "CMAKE_REMOVE_MODULES_LIST", "CMAKE_USE_DIR", "CMAKE_VERBOSE", - "CMAKE_WARN_UNUSED_CLI", "DESKTOP_DATABASE_DIR", "EPATCH_COMMON_OPTS", - "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", - "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "MIMEINFO_DATABASE_DIR", - "NINJAOPTS", "mycmakeargs"]), - - ("cmake-multilib", ["CMAKE_ECLASS"]), - - ("cmake-utils", ["BUILD_DIR", "CMAKE_BINARY", "CMAKE_BUILD_TYPE", - "CMAKE_EXTRA_CACHE_FILE", "CMAKE_IN_SOURCE_BUILD", - "CMAKE_MAKEFILE_GENERATOR", "CMAKE_MIN_VERSION", "CMAKE_REMOVE_MODULES", - "CMAKE_REMOVE_MODULES_LIST", "CMAKE_USE_DIR", - "CMAKE_UTILS_QA_SRC_DIR_READONLY", "CMAKE_VERBOSE", "CMAKE_WARN_UNUSED_CLI", - "DESKTOP_DATABASE_DIR", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", - "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", - "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", - "EPATCH_USER_SOURCE", "MIMEINFO_DATABASE_DIR", "NINJAOPTS", "mycmakeargs"]), - - ("common-lisp", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("common-lisp-common", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", - "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", - "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", - "EPATCH_USER_SOURCE"]), - - ("coreboot-sdk", ["COREBOOT_SDK_PREFIX", "COREBOOT_SDK_PREFIX_arm", - "COREBOOT_SDK_PREFIX_arm64", "COREBOOT_SDK_PREFIX_mips", - "COREBOOT_SDK_PREFIX_nds32", "COREBOOT_SDK_PREFIX_riscv", - "COREBOOT_SDK_PREFIX_x86_32", "COREBOOT_SDK_PREFIX_x86_64"]), - - ("crashid", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("cros-arm64", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", - "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", - "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", - "CROS_WORKON_ALWAYS_LIVE", "CROS_WORKON_COMMIT", "CROS_WORKON_DESTDIR", - "CROS_WORKON_EGIT_BRANCH", "CROS_WORKON_INCREMENTAL_BUILD", - "CROS_WORKON_INPLACE", "CROS_WORKON_LOCALNAME", - "CROS_WORKON_MAKE_COMPILE_ARGS", "CROS_WORKON_MANUAL_UPREV", - "CROS_WORKON_OPTIONAL_CHECKOUT", "CROS_WORKON_OUTOFTREE_BUILD", - "CROS_WORKON_PROJECT", "CROS_WORKON_REPO", "CROS_WORKON_SRCPATH", - "CROS_WORKON_SRCROOT", "CROS_WORKON_SUBDIRS_TO_COPY", - "CROS_WORKON_SUBDIRS_TO_REV", "CROS_WORKON_SUBTREE", "CROS_WORKON_TREE", - "CROS_WORKON_USE_VCSID", "EGIT_BOOTSTRAP", "EGIT_BRANCH", "EGIT_COMMIT", - "EGIT_DIR", "EGIT_HAS_SUBMODULES", "EGIT_MASTER", "EGIT_NONBARE", - "EGIT_NOUNPACK", "EGIT_OPTIONS", "EGIT_PROJECT", "EGIT_PRUNE", - "EGIT_REPACK", "EGIT_REPO_URI", "EGIT_SOURCEDIR", "EGIT_STORE_DIR", - "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", - "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EVCS_OFFLINE"]), - - ("cros-bazel", ["BAZEL_BAZELRC", "BAZEL_CC_BAZELRC", "BAZEL_CC_BUILD", - "BAZEL_CC_CONFIG_DIR", "BAZEL_CC_TOOLCHAIN_CONFIG", - "BAZEL_PORTAGE_PACKAGE_DIR"]), - - ("cros-binary", ["CROS_BINARY_LOCAL_URI_BASE", "CROS_BINARY_URI"]), - - ("cros-common.mk", ["CROS_COMMON_MK_NATIVE_TEST"]), - - ("cros-config-bsp", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", - "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", - "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", - "PROGRAM", "PROJECTS", "UNIBOARD_CROS_CONFIG_DIR", - "UNIBOARD_JSON_INSTALL_PATH", "UNIBOARD_YAML_CONFIG", "UNIBOARD_YAML_DIR"]), - - ("cros-constants", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", - "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", - "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL"]), - - ("cros-cpfe", ["CROS_CPFE_BOARD_OVERLAY", "CROS_CPFE_HOME", - "CROS_CPFE_OVERLAY_NAME", "CROS_CPFE_PATH", "CROS_CPFE_URL", - "CROS_CPFE_USER_NAME"]), - - ("cros-debug", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("cros-ec", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", "AUTOTEST_BASE", - "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", - "COREBOOT_SDK_PREFIX", "COREBOOT_SDK_PREFIX_arm", - "COREBOOT_SDK_PREFIX_arm64", "COREBOOT_SDK_PREFIX_mips", - "COREBOOT_SDK_PREFIX_nds32", "COREBOOT_SDK_PREFIX_riscv", - "COREBOOT_SDK_PREFIX_x86_32", "COREBOOT_SDK_PREFIX_x86_64", - "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", - "CROS_WORKON_ALWAYS_LIVE", "CROS_WORKON_COMMIT", "CROS_WORKON_DESTDIR", - "CROS_WORKON_EGIT_BRANCH", "CROS_WORKON_INCREMENTAL_BUILD", - "CROS_WORKON_INPLACE", "CROS_WORKON_LOCALNAME", - "CROS_WORKON_MAKE_COMPILE_ARGS", "CROS_WORKON_MANUAL_UPREV", - "CROS_WORKON_OPTIONAL_CHECKOUT", "CROS_WORKON_OUTOFTREE_BUILD", - "CROS_WORKON_PROJECT", "CROS_WORKON_REPO", "CROS_WORKON_SRCPATH", - "CROS_WORKON_SRCROOT", "CROS_WORKON_SUBDIRS_TO_COPY", - "CROS_WORKON_SUBDIRS_TO_REV", "CROS_WORKON_SUBTREE", "CROS_WORKON_TREE", - "CROS_WORKON_USE_VCSID", "EC_BOARDS", "EGIT_BOOTSTRAP", "EGIT_BRANCH", - "EGIT_COMMIT", "EGIT_DIR", "EGIT_HAS_SUBMODULES", "EGIT_MASTER", - "EGIT_NONBARE", "EGIT_NOUNPACK", "EGIT_OPTIONS", "EGIT_PROJECT", - "EGIT_PRUNE", "EGIT_REPACK", "EGIT_REPO_URI", "EGIT_SOURCEDIR", - "EGIT_STORE_DIR", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", - "EVCS_OFFLINE", "UNIBOARD_CROS_CONFIG_DIR", "UNIBOARD_JSON_INSTALL_PATH", - "UNIBOARD_YAML_CONFIG", "UNIBOARD_YAML_DIR"]), - - ("cros-ec-board", ["EC_BOARDS", "UNIBOARD_CROS_CONFIG_DIR", - "UNIBOARD_JSON_INSTALL_PATH", "UNIBOARD_YAML_CONFIG", "UNIBOARD_YAML_DIR"]), - - ("cros-ec-release", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", - "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", - "COREBOOT_SDK_PREFIX", "COREBOOT_SDK_PREFIX_arm", - "COREBOOT_SDK_PREFIX_arm64", "COREBOOT_SDK_PREFIX_mips", - "COREBOOT_SDK_PREFIX_nds32", "COREBOOT_SDK_PREFIX_riscv", - "COREBOOT_SDK_PREFIX_x86_32", "COREBOOT_SDK_PREFIX_x86_64", - "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", - "CROS_WORKON_ALWAYS_LIVE", "CROS_WORKON_COMMIT", "CROS_WORKON_DESTDIR", - "CROS_WORKON_EGIT_BRANCH", "CROS_WORKON_INCREMENTAL_BUILD", - "CROS_WORKON_INPLACE", "CROS_WORKON_LOCALNAME", - "CROS_WORKON_MAKE_COMPILE_ARGS", "CROS_WORKON_MANUAL_UPREV", - "CROS_WORKON_OPTIONAL_CHECKOUT", "CROS_WORKON_OUTOFTREE_BUILD", - "CROS_WORKON_PROJECT", "CROS_WORKON_REPO", "CROS_WORKON_SRCPATH", - "CROS_WORKON_SRCROOT", "CROS_WORKON_SUBDIRS_TO_COPY", - "CROS_WORKON_SUBDIRS_TO_REV", "CROS_WORKON_SUBTREE", "CROS_WORKON_TREE", - "CROS_WORKON_USE_VCSID", "EC_BOARDS", "EGIT_BOOTSTRAP", "EGIT_BRANCH", - "EGIT_COMMIT", "EGIT_DIR", "EGIT_HAS_SUBMODULES", "EGIT_MASTER", - "EGIT_NONBARE", "EGIT_NOUNPACK", "EGIT_OPTIONS", "EGIT_PROJECT", - "EGIT_PRUNE", "EGIT_REPACK", "EGIT_REPO_URI", "EGIT_SOURCEDIR", - "EGIT_STORE_DIR", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", - "EVCS_OFFLINE", "FIRMWARE_EC_BOARD", "FIRMWARE_EC_RELEASE_REPLACE_RO", - "UNIBOARD_CROS_CONFIG_DIR", "UNIBOARD_JSON_INSTALL_PATH", - "UNIBOARD_YAML_CONFIG", "UNIBOARD_YAML_DIR"]), - - ("cros-factory", ["CROS_FACTORY_BOARD_RESOURCES_DIR"]), - - ("cros-factory-board", ["CROS_FACTORY_BOARD_RESOURCES_DIR"]), - - ("cros-firmware", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", - "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", - "CROS_FIRMWARE_BCS_OVERLAY", "CROS_FIRMWARE_EC_IMAGE", - "CROS_FIRMWARE_MAIN_IMAGE", "CROS_FIRMWARE_MAIN_RW_IMAGE", - "CROS_FIRMWARE_PD_IMAGE", "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", - "CROS_GIT_INT_HOST_URL", "CROS_WORKON_ALWAYS_LIVE", "CROS_WORKON_COMMIT", - "CROS_WORKON_DESTDIR", "CROS_WORKON_EGIT_BRANCH", - "CROS_WORKON_INCREMENTAL_BUILD", "CROS_WORKON_INPLACE", - "CROS_WORKON_LOCALNAME", "CROS_WORKON_MAKE_COMPILE_ARGS", - "CROS_WORKON_MANUAL_UPREV", "CROS_WORKON_OPTIONAL_CHECKOUT", - "CROS_WORKON_OUTOFTREE_BUILD", "CROS_WORKON_PROJECT", "CROS_WORKON_REPO", - "CROS_WORKON_SRCPATH", "CROS_WORKON_SRCROOT", "CROS_WORKON_SUBDIRS_TO_COPY", - "CROS_WORKON_SUBDIRS_TO_REV", "CROS_WORKON_SUBTREE", "CROS_WORKON_TREE", - "CROS_WORKON_USE_VCSID", "EGIT_BOOTSTRAP", "EGIT_BRANCH", "EGIT_COMMIT", - "EGIT_DIR", "EGIT_HAS_SUBMODULES", "EGIT_MASTER", "EGIT_NONBARE", - "EGIT_NOUNPACK", "EGIT_OPTIONS", "EGIT_PROJECT", "EGIT_PRUNE", - "EGIT_REPACK", "EGIT_REPO_URI", "EGIT_SOURCEDIR", "EGIT_STORE_DIR", - "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", - "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EVCS_OFFLINE", - "UNIBOARD_CROS_CONFIG_DIR", "UNIBOARD_JSON_INSTALL_PATH", - "UNIBOARD_YAML_CONFIG", "UNIBOARD_YAML_DIR"]), - - ("cros-fuzzer", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("cros-fwupd", ["CROS_FWUPD_URL"]), - - ("cros-go", ["CROS_GO_BINARIES", "CROS_GO_PACKAGES", - "CROS_GO_SKIP_DEP_CHECK", "CROS_GO_SOURCE", "CROS_GO_TEST", - "CROS_GO_VERSION", "CROS_GO_VET", "CROS_GO_VET_FLAGS", "CROS_GO_WORKSPACE"]), - - ("cros-i686", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", - "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", - "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", - "CROS_WORKON_ALWAYS_LIVE", "CROS_WORKON_COMMIT", "CROS_WORKON_DESTDIR", - "CROS_WORKON_EGIT_BRANCH", "CROS_WORKON_INCREMENTAL_BUILD", - "CROS_WORKON_INPLACE", "CROS_WORKON_LOCALNAME", - "CROS_WORKON_MAKE_COMPILE_ARGS", "CROS_WORKON_MANUAL_UPREV", - "CROS_WORKON_OPTIONAL_CHECKOUT", "CROS_WORKON_OUTOFTREE_BUILD", - "CROS_WORKON_PROJECT", "CROS_WORKON_REPO", "CROS_WORKON_SRCPATH", - "CROS_WORKON_SRCROOT", "CROS_WORKON_SUBDIRS_TO_COPY", - "CROS_WORKON_SUBDIRS_TO_REV", "CROS_WORKON_SUBTREE", "CROS_WORKON_TREE", - "CROS_WORKON_USE_VCSID", "EGIT_BOOTSTRAP", "EGIT_BRANCH", "EGIT_COMMIT", - "EGIT_DIR", "EGIT_HAS_SUBMODULES", "EGIT_MASTER", "EGIT_NONBARE", - "EGIT_NOUNPACK", "EGIT_OPTIONS", "EGIT_PROJECT", "EGIT_PRUNE", - "EGIT_REPACK", "EGIT_REPO_URI", "EGIT_SOURCEDIR", "EGIT_STORE_DIR", - "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", - "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EVCS_OFFLINE"]), - - ("cros-ish", ["COREBOOT_SDK_PREFIX", "COREBOOT_SDK_PREFIX_arm", - "COREBOOT_SDK_PREFIX_arm64", "COREBOOT_SDK_PREFIX_mips", - "COREBOOT_SDK_PREFIX_nds32", "COREBOOT_SDK_PREFIX_riscv", - "COREBOOT_SDK_PREFIX_x86_32", "COREBOOT_SDK_PREFIX_x86_64", "ISH_TARGETS", - "UNIBOARD_CROS_CONFIG_DIR", "UNIBOARD_JSON_INSTALL_PATH", - "UNIBOARD_YAML_CONFIG", "UNIBOARD_YAML_DIR"]), - - ("cros-kernel-info", ["CONFIG_CHECK", "KBUILD_OUTPUT", "KERNEL_DIR", - "KV_DIR", "KV_EXTRA", "KV_FULL", "KV_LOCAL", "KV_MAJOR", "KV_MINOR", - "KV_OUT_DIR", "KV_PATCH", "_LINUX_CONFIG_EXISTS_DONE"]), - - ("cros-racc", ["CROS_RACC_MODEL"]), - - ("cros-rust", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", - "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", - "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", - "CROS_RUST_CRATE_NAME", "CROS_RUST_CRATE_VERSION", "CROS_RUST_EMPTY_CRATE", - "CROS_RUST_EMPTY_CRATE_FEATURES", "CROS_RUST_HOST_TESTS", - "CROS_RUST_OVERFLOW_CHECKS", "CROS_RUST_PACKAGE_IS_HOT", - "CROS_RUST_PLATFORM_TEST_ARGS", "CROS_RUST_PREINSTALLED_REGISTRY_CRATE", - "CROS_RUST_REMOVE_DEV_DEPS", "CROS_RUST_REMOVE_TARGET_CFG", - "CROS_RUST_SUBDIR", "CROS_RUST_TESTS", "CROS_RUST_TEST_DIRECT_EXEC_ONLY", - "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", - "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("cros-rustc", ["CROS_RUSTC_BUILD_RAW_SOURCES", "EPATCH_COMMON_OPTS", - "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", - "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EPYTHON", "PYTHON", - "PYTHON_COMPAT", "PYTHON_COMPAT_NO_STRICT", "PYTHON_COMPAT_OVERRIDE", - "PYTHON_DEPS", "PYTHON_REQ_USE", "PYTHON_USEDEP", - "RUSTC_BARE_TARGET_TRIPLES", "RUSTC_TARGET_TRIPLES", "_PYTHON_ALL_IMPLS"]), - - ("cros-sanitizers", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("cros-unibuild", ["UNIBOARD_CROS_CONFIG_DIR", "UNIBOARD_JSON_INSTALL_PATH", - "UNIBOARD_YAML_CONFIG", "UNIBOARD_YAML_DIR"]), - - ("cros-workon", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", - "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", - "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", - "CROS_WORKON_ALWAYS_LIVE", "CROS_WORKON_COMMIT", "CROS_WORKON_DESTDIR", - "CROS_WORKON_EGIT_BRANCH", "CROS_WORKON_INCREMENTAL_BUILD", - "CROS_WORKON_INPLACE", "CROS_WORKON_LOCALNAME", - "CROS_WORKON_MAKE_COMPILE_ARGS", "CROS_WORKON_MANUAL_UPREV", - "CROS_WORKON_OPTIONAL_CHECKOUT", "CROS_WORKON_OUTOFTREE_BUILD", - "CROS_WORKON_PROJECT", "CROS_WORKON_REPO", "CROS_WORKON_SRCPATH", - "CROS_WORKON_SRCROOT", "CROS_WORKON_SUBDIRS_TO_COPY", - "CROS_WORKON_SUBDIRS_TO_REV", "CROS_WORKON_SUBTREE", "CROS_WORKON_TREE", - "CROS_WORKON_USE_VCSID", "EGIT_BOOTSTRAP", "EGIT_BRANCH", "EGIT_COMMIT", - "EGIT_DIR", "EGIT_HAS_SUBMODULES", "EGIT_MASTER", "EGIT_NONBARE", - "EGIT_NOUNPACK", "EGIT_OPTIONS", "EGIT_PROJECT", "EGIT_PRUNE", - "EGIT_REPACK", "EGIT_REPO_URI", "EGIT_SOURCEDIR", "EGIT_STORE_DIR", - "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", - "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EVCS_OFFLINE"]), - - ("cuda", ["CUDA_VERBOSE", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", - "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", - "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", - "EPATCH_USER_SOURCE", "NVCCFLAGS"]), - - ("distutils-r1", ["BUILD_DIR", "DESKTOP_DATABASE_DIR", - "DISTUTILS_ALL_SUBPHASE_IMPLS", "DISTUTILS_IN_SOURCE_BUILD", - "DISTUTILS_OPTIONAL", "DISTUTILS_SINGLE_IMPL", "DISTUTILS_USE_SETUPTOOLS", - "DOCS", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EPYTHON", - "EXAMPLES", "HTML_DOCS", "MIMEINFO_DATABASE_DIR", "MULTIBUILD_ID", - "MULTIBUILD_VARIANT", "MULTIBUILD_VARIANTS", "PATCHES", "PYTHON", - "PYTHON_COMPAT", "PYTHON_COMPAT_NO_STRICT", "PYTHON_COMPAT_OVERRIDE", - "PYTHON_DEPS", "PYTHON_MULTI_USEDEP", "PYTHON_REQUIRED_USE", - "PYTHON_REQ_USE", "PYTHON_SINGLE_USEDEP", "PYTHON_USEDEP", - "_PYTHON_ALL_IMPLS", "mydistutilsargs"]), - - ("dlc", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", "AUTOTEST_BASE", - "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", - "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", - "DLC_CRITICAL_UPDATE", "DLC_DAYS_TO_PURGE", "DLC_DESCRIPTION", - "DLC_ENABLED", "DLC_FACTORY_INSTALL", "DLC_FS_TYPE", "DLC_ID", - "DLC_LOADPIN_VERITY_DIGEST", "DLC_MOUNT_FILE_REQUIRED", "DLC_NAME", - "DLC_PACKAGE", "DLC_PREALLOC_BLOCKS", "DLC_PRELOAD", "DLC_RESERVED", - "DLC_SCALED", "DLC_USED_BY", "DLC_VERSION"]), - - ("elisp", ["BYTECOMPFLAGS", "DOCS", "ELISP_PATCHES", "ELISP_TEXINFO", - "EMACS", "EMACSFLAGS", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", - "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", - "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", - "EPATCH_USER_SOURCE", "NEED_EMACS", "SITEETC", "SITEFILE", "SITELISP"]), - - ("elisp-common", ["BYTECOMPFLAGS", "EMACS", "EMACSFLAGS", "SITEETC", - "SITELISP"]), - - ("emboss", ["AM_OPTS", "AT_M4DIR", "AT_NOEAUTOMAKE", "AT_NOELIBTOOLIZE", - "AT_SYS_M4DIR", "AUTOTOOLS_AUTO_DEPEND", "EBO_DESCRIPTION", - "EBO_EAUTORECONF", "EBO_EXTRA_ECONF", "EPATCH_COMMON_OPTS", - "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", - "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "WANT_AUTOCONF", - "WANT_AUTOMAKE", "WANT_LIBTOOL", "_LATEST_AUTOMAKE"]), - - ("epatch", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("epunt-cxx", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("eutils", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("fcaps", ["FILECAPS"]), - - ("flag-o-matic", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("fortran", ["AM_OPTS", "AT_M4DIR", "AT_NOEAUTOMAKE", "AT_NOELIBTOOLIZE", - "AT_SYS_M4DIR", "AUTOTOOLS_AUTO_DEPEND", "EPATCH_COMMON_OPTS", - "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", - "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "WANT_AUTOCONF", - "WANT_AUTOMAKE", "WANT_LIBTOOL", "_LATEST_AUTOMAKE"]), - - ("fortran-2", ["FORTRAN_NEEDED", "FORTRAN_NEED_OPENMP", "FORTRAN_STANDARD"]), - - ("games", ["DOCS", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "HTML_DOCS", - "PATCHES"]), - - ("git-2", ["EGIT_BOOTSTRAP", "EGIT_BRANCH", "EGIT_COMMIT", "EGIT_DIR", - "EGIT_HAS_SUBMODULES", "EGIT_MASTER", "EGIT_NONBARE", "EGIT_NOUNPACK", - "EGIT_OPTIONS", "EGIT_PROJECT", "EGIT_PRUNE", "EGIT_REPACK", - "EGIT_REPO_URI", "EGIT_SOURCEDIR", "EGIT_STORE_DIR", "EVCS_OFFLINE"]), - - ("git-r3", ["EGIT3_STORE_DIR", "EGIT_BRANCH", "EGIT_CHECKOUT_DIR", - "EGIT_CLONE_TYPE", "EGIT_COMMIT", "EGIT_COMMIT_DATE", "EGIT_MIN_CLONE_TYPE", - "EGIT_MIRROR_URI", "EGIT_REPO_URI", "EGIT_SUBMODULES", "EVCS_OFFLINE", - "EVCS_UMASK"]), - - ("gnome.org", ["GNOME_ORG_MODULE", "GNOME_ORG_PVP", "GNOME_TARBALL_SUFFIX"]), - - ("gnome2", ["AM_OPTS", "AT_M4DIR", "AT_NOEAUTOMAKE", "AT_NOELIBTOOLIZE", - "AT_SYS_M4DIR", "AUTOTOOLS_AUTO_DEPEND", "EPATCH_COMMON_OPTS", - "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", - "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "WANT_AUTOCONF", - "WANT_AUTOMAKE", "WANT_LIBTOOL", "_LATEST_AUTOMAKE"]), - - ("gnome2-utils", ["DESKTOP_DATABASE_DIR", "EPATCH_COMMON_OPTS", - "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", - "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "GCONFTOOL_BIN", - "GLIB_COMPILE_SCHEMAS", "GNOME2_ECLASS_GDK_PIXBUF_LOADERS", - "GNOME2_ECLASS_GLIB_SCHEMAS", "GNOME2_ECLASS_ICONS", - "GNOME2_ECLASS_SCHEMAS", "GNOME2_ECLASS_SCROLLS", "MIMEINFO_DATABASE_DIR", - "SCROLLKEEPER_DIR", "SCROLLKEEPER_UPDATE_BIN"]), - - ("go-module", ["EGO_SUM", "_GOMODULE_GOPROXY_BASEURI", - "_GOMODULE_GOSUM_REVERSE_MAP"]), - - ("golang-base", ["EGO_PN"]), - - ("golang-build", ["EGO_BUILD_FLAGS", "EGO_PN"]), - - ("golang-vcs", ["EGO_PN", "EGO_STORE_DIR", "EPATCH_COMMON_OPTS", - "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", - "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EVCS_OFFLINE", "EVCS_UMASK"]), - - ("golang-vcs-snapshot", ["EGO_PN", "EGO_VENDOR"]), - - ("gtest", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", "AUTOTEST_BASE", - "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", - "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", - "GTEST_METADATA_INSTALL_DIR"]), - - ("haskell-cabal", ["CABAL_DEBUG_LOOSENING", "CABAL_EXTRA_BUILD_FLAGS", - "CABAL_EXTRA_CONFIGURE_FLAGS", "CABAL_EXTRA_TEST_FLAGS", - "CABAL_REPORT_OTHER_BROKEN_PACKAGES", "EPATCH_COMMON_OPTS", - "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", - "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "GHC_BOOTSTRAP_FLAGS"]), - - ("java-ant-2", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", - "JAVA_ANT_CLASSPATH_TAGS", "JAVA_ANT_DISABLE_ANT_CORE_DEP", - "JAVA_ANT_IGNORE_SYSTEM_CLASSES", "JAVA_PKG_ALLOW_VM_CHANGE", - "JAVA_PKG_BSFIX", "JAVA_PKG_BSFIX_ALL", "JAVA_PKG_BSFIX_NAME", - "JAVA_PKG_BSFIX_SOURCE_TAGS", "JAVA_PKG_BSFIX_TARGET_TAGS", - "JAVA_PKG_COMPILERS_CONF", "JAVA_PKG_COMPILER_DIR", "JAVA_PKG_DEBUG", - "JAVA_PKG_E_DEPEND", "JAVA_PKG_FORCE_ANT_TASKS", "JAVA_PKG_FORCE_COMPILER", - "JAVA_PKG_FORCE_VM", "JAVA_PKG_WANT_BOOTCLASSPATH", - "JAVA_PKG_WANT_BUILD_VM", "JAVA_PKG_WANT_SOURCE", "JAVA_PKG_WANT_TARGET", - "JAVA_RM_FILES", "WANT_ANT_TASKS"]), - - ("java-pkg-2", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", - "JAVA_PKG_ALLOW_VM_CHANGE", "JAVA_PKG_COMPILERS_CONF", - "JAVA_PKG_COMPILER_DIR", "JAVA_PKG_DEBUG", "JAVA_PKG_E_DEPEND", - "JAVA_PKG_FORCE_ANT_TASKS", "JAVA_PKG_FORCE_COMPILER", "JAVA_PKG_FORCE_VM", - "JAVA_PKG_IUSE", "JAVA_PKG_WANT_BOOTCLASSPATH", "JAVA_PKG_WANT_BUILD_VM", - "JAVA_PKG_WANT_SOURCE", "JAVA_PKG_WANT_TARGET", "JAVA_RM_FILES"]), - - ("java-pkg-opt-2", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", - "JAVA_PKG_ALLOW_VM_CHANGE", "JAVA_PKG_COMPILERS_CONF", - "JAVA_PKG_COMPILER_DIR", "JAVA_PKG_DEBUG", "JAVA_PKG_E_DEPEND", - "JAVA_PKG_FORCE_ANT_TASKS", "JAVA_PKG_FORCE_COMPILER", "JAVA_PKG_FORCE_VM", - "JAVA_PKG_OPT_USE", "JAVA_PKG_WANT_BOOTCLASSPATH", "JAVA_PKG_WANT_BUILD_VM", - "JAVA_PKG_WANT_SOURCE", "JAVA_PKG_WANT_TARGET", "JAVA_RM_FILES"]), - - ("java-utils-2", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", - "JAVA_PKG_ALLOW_VM_CHANGE", "JAVA_PKG_COMPILERS_CONF", - "JAVA_PKG_COMPILER_DIR", "JAVA_PKG_DEBUG", "JAVA_PKG_E_DEPEND", - "JAVA_PKG_FORCE_ANT_TASKS", "JAVA_PKG_FORCE_COMPILER", "JAVA_PKG_FORCE_VM", - "JAVA_PKG_WANT_BOOTCLASSPATH", "JAVA_PKG_WANT_BUILD_VM", - "JAVA_PKG_WANT_SOURCE", "JAVA_PKG_WANT_TARGET", "JAVA_RM_FILES"]), - - ("java-vm-2", ["EPREFIX", "JAVA_VM_BUILD_ONLY", "JAVA_VM_CONFIG_DIR", - "JAVA_VM_DIR", "JAVA_VM_SYSTEM"]), - - ("kernel-2", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("l10n", ["PLOCALES", "PLOCALE_BACKUP"]), - - ("libchrome", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("linux-info", ["CONFIG_CHECK", "KBUILD_OUTPUT", "KERNEL_DIR", "KV_DIR", - "KV_EXTRA", "KV_FULL", "KV_LOCAL", "KV_MAJOR", "KV_MINOR", "KV_OUT_DIR", - "KV_PATCH", "_LINUX_CONFIG_EXISTS_DONE"]), - - ("linux-mod", ["BUILD_PARAMS", "BUILD_TARGETS", "CONFIG_CHECK", - "ECONF_PARAMS", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", - "KBUILD_OUTPUT", "KERNEL_DIR", "KV_DIR", "KV_EXTRA", "KV_FULL", "KV_LOCAL", - "KV_MAJOR", "KV_MINOR", "KV_OBJ", "KV_OUT_DIR", "KV_PATCH", - "MODULES_OPTIONAL_USE", "MODULE_NAMES", "_LINUX_CONFIG_EXISTS_DONE"]), - - ("llvm", ["LLVM_MAX_SLOT", "_LLVM_KNOWN_SLOTS"]), - - ("llvm.org", ["EGIT3_STORE_DIR", "EGIT_BRANCH", "EGIT_CHECKOUT_DIR", - "EGIT_CLONE_TYPE", "EGIT_COMMIT", "EGIT_COMMIT_DATE", "EGIT_MIN_CLONE_TYPE", - "EGIT_MIRROR_URI", "EGIT_REPO_URI", "EGIT_SUBMODULES", "EVCS_OFFLINE", - "EVCS_UMASK", "LLVM_COMPONENTS", "LLVM_TEST_COMPONENTS", - "_LLVM_MASTER_MAJOR", "_LLVM_SOURCE_TYPE"]), - - ("lua-single", ["ELUA", "LUA", "LUA_COMPAT", "LUA_COMPAT_OVERRIDE", - "LUA_DEPS", "LUA_REQUIRED_USE", "LUA_REQ_USE", "LUA_SINGLE_USEDEP", - "LUA_USEDEP", "_LUA_ALL_IMPLS", "_LUA_HISTORICAL_IMPLS"]), - - ("lua-utils", ["ELUA", "LUA", "_LUA_ALL_IMPLS", "_LUA_HISTORICAL_IMPLS"]), - - ("mercurial", ["EHG_CLONE_CMD", "EHG_OFFLINE", "EHG_PROJECT", - "EHG_PULL_CMD", "EHG_QUIET", "EHG_REPO_URI", "EHG_REVISION", - "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", - "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("meson", ["BUILD_DIR", "EMESON_BUILDTYPE", "EMESON_SOURCE", - "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", - "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EPYTHON", "MYMESONARGS", - "NINJAOPTS", "PYTHON", "PYTHON_COMPAT_NO_STRICT", "_PYTHON_ALL_IMPLS", - "emesonargs"]), - - ("meson-multilib", ["BUILD_DIR", "EMESON_BUILDTYPE", "EMESON_SOURCE", - "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", - "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EPYTHON", "MULTIBUILD_ID", - "MULTIBUILD_VARIANT", "MULTIBUILD_VARIANTS", "MULTILIB_ABI_FLAG", - "MULTILIB_CHOST_TOOLS", "MULTILIB_COMPAT", "MULTILIB_USEDEP", - "MULTILIB_WRAPPED_HEADERS", "MYMESONARGS", "NINJAOPTS", "PYTHON", - "PYTHON_COMPAT_NO_STRICT", "_MULTILIB_FLAGS", "_PYTHON_ALL_IMPLS", - "emesonargs"]), - - ("multibuild", ["BUILD_DIR", "MULTIBUILD_ID", "MULTIBUILD_VARIANT", - "MULTIBUILD_VARIANTS"]), - - ("multilib-build", ["BUILD_DIR", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", - "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", - "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", - "EPATCH_USER_SOURCE", "MULTIBUILD_ID", "MULTIBUILD_VARIANT", - "MULTIBUILD_VARIANTS", "MULTILIB_ABI_FLAG", "MULTILIB_CHOST_TOOLS", - "MULTILIB_COMPAT", "MULTILIB_USEDEP", "MULTILIB_WRAPPED_HEADERS", - "_MULTILIB_FLAGS"]), - - ("multilib-minimal", ["BUILD_DIR", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", - "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", - "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", - "EPATCH_USER_SOURCE", "MULTIBUILD_ID", "MULTIBUILD_VARIANT", - "MULTIBUILD_VARIANTS", "MULTILIB_ABI_FLAG", "MULTILIB_CHOST_TOOLS", - "MULTILIB_COMPAT", "MULTILIB_USEDEP", "MULTILIB_WRAPPED_HEADERS", - "_MULTILIB_FLAGS"]), - - ("ninja-utils", ["NINJAOPTS"]), - - ("obs-download", ["OBS_PACKAGE", "OBS_PROJECT", "OPENSUSE_RELEASE"]), - - ("obs-service", ["ADDITIONAL_FILES", "OBS_PACKAGE", "OBS_PROJECT", - "OBS_SERVICE_NAME", "OPENSUSE_RELEASE"]), - - ("osreleased", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("pam", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("perl-app", ["ALTERNATIVES", "DIST_A", "DIST_AUTHOR", "DIST_A_EXT", - "DIST_EXAMPLES", "DIST_NAME", "DIST_SECTION", "DIST_TEST", - "DIST_TEST_OVERRIDE", "DIST_VERSION", "EPATCH_COMMON_OPTS", - "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", - "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "GENTOO_DEPEND_ON_PERL", - "SOURCE", "UNPACKER_BZ2"]), - - ("perl-functions", ["ALTERNATIVES", "SOURCE"]), - - ("perl-module", ["ALTERNATIVES", "DIST_A", "DIST_AUTHOR", "DIST_A_EXT", - "DIST_EXAMPLES", "DIST_NAME", "DIST_SECTION", "DIST_TEST", - "DIST_TEST_OVERRIDE", "DIST_VERSION", "EPATCH_COMMON_OPTS", - "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", - "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "GENTOO_DEPEND_ON_PERL", - "SOURCE", "UNPACKER_BZ2"]), - - ("php-pear", ["PEAR_PV", "PHP_PEAR_PKG_NAME"]), - - ("php-pear-r1", ["PEAR_PV", "PHP_PEAR_PKG_NAME"]), - - ("platform", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", - "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", - "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", - "CROS_WORKON_ALWAYS_LIVE", "CROS_WORKON_COMMIT", "CROS_WORKON_DESTDIR", - "CROS_WORKON_EGIT_BRANCH", "CROS_WORKON_INCREMENTAL_BUILD", - "CROS_WORKON_INPLACE", "CROS_WORKON_LOCALNAME", - "CROS_WORKON_MAKE_COMPILE_ARGS", "CROS_WORKON_MANUAL_UPREV", - "CROS_WORKON_OPTIONAL_CHECKOUT", "CROS_WORKON_OUTOFTREE_BUILD", - "CROS_WORKON_PROJECT", "CROS_WORKON_REPO", "CROS_WORKON_SRCPATH", - "CROS_WORKON_SRCROOT", "CROS_WORKON_SUBDIRS_TO_COPY", - "CROS_WORKON_SUBDIRS_TO_REV", "CROS_WORKON_SUBTREE", "CROS_WORKON_TREE", - "CROS_WORKON_USE_VCSID", "EGIT_BOOTSTRAP", "EGIT_BRANCH", "EGIT_COMMIT", - "EGIT_DIR", "EGIT_HAS_SUBMODULES", "EGIT_MASTER", "EGIT_NONBARE", - "EGIT_NOUNPACK", "EGIT_OPTIONS", "EGIT_PROJECT", "EGIT_PRUNE", - "EGIT_REPACK", "EGIT_REPO_URI", "EGIT_SOURCEDIR", "EGIT_STORE_DIR", - "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", - "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EVCS_OFFLINE", "OUT", - "PLATFORM_ARC_BUILD", "PLATFORM_BUILD", "PLATFORM_NATIVE_TEST", - "PLATFORM_SUBDIR", "WANT_LIBBRILLO", "WANT_LIBCHROME"]), - - ("plocale", ["PLOCALES", "PLOCALE_BACKUP"]), - - ("prefix", ["EPREFIX"]), - - ("python-any-r1", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EPYTHON", - "PYTHON", "PYTHON_COMPAT", "PYTHON_COMPAT_NO_STRICT", - "PYTHON_COMPAT_OVERRIDE", "PYTHON_DEPS", "PYTHON_REQ_USE", "PYTHON_USEDEP", - "_PYTHON_ALL_IMPLS"]), - - ("python-r1", ["BUILD_DIR", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", - "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", - "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", - "EPATCH_USER_SOURCE", "EPYTHON", "MULTIBUILD_ID", "MULTIBUILD_VARIANT", - "MULTIBUILD_VARIANTS", "PYTHON", "PYTHON_COMPAT", "PYTHON_COMPAT_NO_STRICT", - "PYTHON_COMPAT_OVERRIDE", "PYTHON_DEPS", "PYTHON_REQUIRED_USE", - "PYTHON_REQ_USE", "PYTHON_USEDEP", "_PYTHON_ALL_IMPLS"]), - - ("python-single-r1", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", - "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", - "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", - "EPATCH_USER_SOURCE", "EPYTHON", "PYTHON", "PYTHON_COMPAT", - "PYTHON_COMPAT_NO_STRICT", "PYTHON_COMPAT_OVERRIDE", "PYTHON_DEPS", - "PYTHON_MULTI_USEDEP", "PYTHON_REQUIRED_USE", "PYTHON_REQ_USE", - "PYTHON_SINGLE_USEDEP", "PYTHON_USEDEP", "_PYTHON_ALL_IMPLS"]), - - ("python-utils-r1", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EPYTHON", - "PYTHON", "PYTHON_COMPAT_NO_STRICT", "_PYTHON_ALL_IMPLS"]), - - ("qmake-utils", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", - "EQMAKE4_EXCLUDE"]), - - ("readme.gentoo", ["DISABLE_AUTOFORMATTING", "EPATCH_COMMON_OPTS", - "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", - "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "FORCE_PRINT_ELOG", - "README_GENTOO_SUFFIX"]), - - ("readme.gentoo-r1", ["DISABLE_AUTOFORMATTING", "FORCE_PRINT_ELOG", - "README_GENTOO_SUFFIX"]), - - ("rpm", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE"]), - - ("ruby", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "PATCHES"]), - - ("ruby-ng", ["RUBY_OPTIONAL", "RUBY_PATCHES", "RUBY_QA_ALLOWED_LIBS", - "RUBY_S", "USE_RUBY"]), - - ("ruby-single", ["RUBY_DEPS", "RUBY_TARGETS_PREFERENCE", "USE_RUBY"]), - - ("ruby-utils", ["RUBY_TARGETS_PREFERENCE"]), - - ("scons-utils", ["EXTRA_ESCONS", "SCONSOPTS", "SCONS_MIN_VERSION", - "USE_SCONS_FALSE", "USE_SCONS_TRUE", "myesconsargs"]), - - ("sgml-catalog", ["DOCS", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", - "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", - "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", - "EPATCH_USER_SOURCE", "HTML_DOCS", "PATCHES"]), - - ("tast-bundle", ["ARC_ETC_DIR", "ARC_PREFIX", "ARC_VENDOR_DIR", - "AUTOTEST_BASE", "CHROMITE_BIN_DIR", "CHROMITE_DIR", "CHROOT_SOURCE_ROOT", - "CROS_GIT_AOSP_URL", "CROS_GIT_HOST_URL", "CROS_GIT_INT_HOST_URL", - "CROS_GO_BINARIES", "CROS_GO_PACKAGES", "CROS_GO_SKIP_DEP_CHECK", - "CROS_GO_SOURCE", "CROS_GO_TEST", "CROS_GO_VERSION", "CROS_GO_VET", - "CROS_GO_VET_FLAGS", "CROS_GO_WORKSPACE", "CROS_WORKON_ALWAYS_LIVE", - "CROS_WORKON_COMMIT", "CROS_WORKON_DESTDIR", "CROS_WORKON_EGIT_BRANCH", - "CROS_WORKON_INCREMENTAL_BUILD", "CROS_WORKON_INPLACE", - "CROS_WORKON_LOCALNAME", "CROS_WORKON_MAKE_COMPILE_ARGS", - "CROS_WORKON_MANUAL_UPREV", "CROS_WORKON_OPTIONAL_CHECKOUT", - "CROS_WORKON_OUTOFTREE_BUILD", "CROS_WORKON_PROJECT", "CROS_WORKON_REPO", - "CROS_WORKON_SRCPATH", "CROS_WORKON_SRCROOT", "CROS_WORKON_SUBDIRS_TO_COPY", - "CROS_WORKON_SUBDIRS_TO_REV", "CROS_WORKON_SUBTREE", "CROS_WORKON_TREE", - "CROS_WORKON_USE_VCSID", "EGIT_BOOTSTRAP", "EGIT_BRANCH", "EGIT_COMMIT", - "EGIT_DIR", "EGIT_HAS_SUBMODULES", "EGIT_MASTER", "EGIT_NONBARE", - "EGIT_NOUNPACK", "EGIT_OPTIONS", "EGIT_PROJECT", "EGIT_PRUNE", - "EGIT_REPACK", "EGIT_REPO_URI", "EGIT_SOURCEDIR", "EGIT_STORE_DIR", - "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", - "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EVCS_OFFLINE", - "TAST_BUNDLE_EXCLUDE_DATA_FILES", "TAST_BUNDLE_PRIVATE"]), - - ("tegra-bct", ["TEGRA_BCT_CHIP_FAMILY", "TEGRA_BCT_FLASH_CONFIG", - "TEGRA_BCT_ODM_DATA_CONFIG", "TEGRA_BCT_SDRAM_CONFIG"]), - - ("toolchain", ["EGIT_BOOTSTRAP", "EGIT_BRANCH", "EGIT_COMMIT", "EGIT_DIR", - "EGIT_HAS_SUBMODULES", "EGIT_MASTER", "EGIT_NONBARE", "EGIT_NOUNPACK", - "EGIT_OPTIONS", "EGIT_PROJECT", "EGIT_PRUNE", "EGIT_REPACK", - "EGIT_REPO_URI", "EGIT_SOURCEDIR", "EGIT_STORE_DIR", "EPATCH_COMMON_OPTS", - "EPATCH_EXCLUDE", "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", - "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", "EPATCH_SUFFIX", - "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "EVCS_OFFLINE"]), - - ("unpacker", ["UNPACKER_BZ2"]), - - ("vala", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", - "VALA_MAX_API_VERSION", "VALA_MIN_API_VERSION", "VALA_USE_DEPEND"]), - - ("vdr-plugin", ["DOCS", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", - "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", - "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", - "EPATCH_USER_SOURCE", "HTML_DOCS", "PATCHES"]), - - ("verify-sig", ["VERIFY_SIG_OPENPGP_KEYSERVER", - "VERIFY_SIG_OPENPGP_KEY_PATH", "VERIFY_SIG_OPENPGP_KEY_REFRESH"]), - - ("waf-utils", ["WAF_BINARY", "WAF_VERBOSE"]), - - ("webapp", ["WEBAPP_DEPEND", "WEBAPP_NO_AUTO_INSTALL", "WEBAPP_OPTIONAL"]), - - ("wxwidgets", ["EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", "EPATCH_FORCE", - "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", "EPATCH_SOURCE", - "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", "EPATCH_USER_SOURCE", "WX_GTK_VER"]), - - ("xdg", ["DESKTOP_DATABASE_DIR", "MIMEINFO_DATABASE_DIR"]), - - ("xdg-utils", ["DESKTOP_DATABASE_DIR", "MIMEINFO_DATABASE_DIR"]), - - ("xemacs-elisp", ["SIMPLE_ELISP"]), - - ("xorg-2", ["AM_OPTS", "AT_M4DIR", "AT_NOEAUTOMAKE", "AT_NOELIBTOOLIZE", - "AT_SYS_M4DIR", "AUTOTOOLS_AUTORECONF", "AUTOTOOLS_AUTO_DEPEND", - "AUTOTOOLS_IN_SOURCE_BUILD", "AUTOTOOLS_PRUNE_LIBTOOL_FILES", "BUILD_DIR", - "DOCS", "ECONF_SOURCE", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", - "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", - "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", - "EPATCH_USER_SOURCE", "FONT_DIR", "HTML_DOCS", "MULTIBUILD_ID", - "MULTIBUILD_VARIANT", "MULTIBUILD_VARIANTS", "MULTILIB_ABI_FLAG", - "MULTILIB_CHOST_TOOLS", "MULTILIB_COMPAT", "MULTILIB_USEDEP", - "MULTILIB_WRAPPED_HEADERS", "PATCHES", "WANT_AUTOCONF", "WANT_AUTOMAKE", - "WANT_LIBTOOL", "XORG_BASE_INDIVIDUAL_URI", "XORG_CONFIGURE_OPTIONS", - "XORG_DOC", "XORG_DRI", "XORG_EAUTORECONF", "XORG_MODULE", - "XORG_MODULE_REBUILD", "XORG_MULTILIB", "XORG_PACKAGE_NAME", "XORG_STATIC", - "_LATEST_AUTOMAKE", "_MULTILIB_FLAGS", "myeconfargs"]), - - ("xorg-3", ["BUILD_DIR", "EPATCH_COMMON_OPTS", "EPATCH_EXCLUDE", - "EPATCH_FORCE", "EPATCH_MULTI_MSG", "EPATCH_OPTS", "EPATCH_SINGLE_MSG", - "EPATCH_SOURCE", "EPATCH_SUFFIX", "EPATCH_USER_EXCLUDE", - "EPATCH_USER_SOURCE", "FONT_DIR", "MULTIBUILD_ID", "MULTIBUILD_VARIANT", - "MULTIBUILD_VARIANTS", "MULTILIB_ABI_FLAG", "MULTILIB_CHOST_TOOLS", - "MULTILIB_COMPAT", "MULTILIB_USEDEP", "MULTILIB_WRAPPED_HEADERS", - "XORG_BASE_INDIVIDUAL_URI", "XORG_CONFIGURE_OPTIONS", "XORG_DOC", - "XORG_DRI", "XORG_EAUTORECONF", "XORG_MODULE", "XORG_MULTILIB", - "XORG_PACKAGE_NAME", "XORG_STATIC", "XORG_TARBALL_SUFFIX", - "_MULTILIB_FLAGS"]) - ] From dfa920c5d285d3bdaedd5f6bdef6c7aa1cc77f2f Mon Sep 17 00:00:00 2001 From: hololeap Date: Sat, 5 Aug 2023 17:38:01 -0600 Subject: [PATCH 10/10] Switch to attoparsec for gentoo scan Signed-off-by: hololeap --- ShellCheck.cabal | 6 +- src/ShellCheck/Data.hs | 4 +- src/ShellCheck/PortageVariables.hs | 99 ++++++++++++++++++++---------- 3 files changed, 74 insertions(+), 35 deletions(-) diff --git a/ShellCheck.cabal b/ShellCheck.cabal index 3166777..f94952e 100644 --- a/ShellCheck.cabal +++ b/ShellCheck.cabal @@ -66,7 +66,11 @@ library directory >= 1.2.3 && < 1.4, -- When cabal supports it, move this to setup-depends: - process + process, + + -- support for scanning Gentoo eclasses + attoparsec, + text exposed-modules: ShellCheck.AST ShellCheck.ASTLib diff --git a/src/ShellCheck/Data.hs b/src/ShellCheck/Data.hs index 299cbd6..644c2f9 100644 --- a/src/ShellCheck/Data.hs +++ b/src/ShellCheck/Data.hs @@ -25,7 +25,7 @@ Use: import Paths_ShellCheck (version) shellcheckVersion = showVersion version -- VERSIONSTRING - +genericInternalVariables :: [String] genericInternalVariables = [ -- Generic "", "_", "rest", "REST", @@ -153,7 +153,7 @@ eclassVarsFromMap :: EclassMap -> String -> [String] eclassVarsFromMap gMap eclass = Data.Map.findWithDefault [] eclass - gMap + (Data.Map.map (map decodeLenient) gMap) portageInternalVariables :: [String] -> EclassMap -> [String] portageInternalVariables inheritedEclasses gMap = diff --git a/src/ShellCheck/PortageVariables.hs b/src/ShellCheck/PortageVariables.hs index 4e79b8a..3c6f611 100644 --- a/src/ShellCheck/PortageVariables.hs +++ b/src/ShellCheck/PortageVariables.hs @@ -1,3 +1,7 @@ +{-# LANGUAGE ApplicativeDo #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} module ShellCheck.PortageVariables ( RepoName @@ -9,25 +13,37 @@ module ShellCheck.PortageVariables , Eclass(..) , portageVariables , scanRepos + , decodeLenient ) where import Control.Applicative +import Control.Exception (bracket) import Control.Monad -import Control.Monad.Trans.Class +import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Maybe -import Data.Map (Map) +import Data.Attoparsec.ByteString +import qualified Data.Attoparsec.ByteString as A +import Data.Attoparsec.ByteString.Char8 hiding (takeWhile) +import Data.ByteString (ByteString) +import qualified Data.ByteString as B +import Data.Char (ord) import qualified Data.Map as M +import Data.Maybe (fromJust) +import qualified Data.Text as T +import qualified Data.Text.Encoding as T +import qualified Data.Text.Encoding.Error as T import System.Directory (listDirectory) import System.Exit (ExitCode(..)) import System.FilePath +import System.IO (hClose) import System.Process -import Text.Parsec hiding ((<|>)) -import Text.Parsec.String -type RepoName = String -type RepoPath = FilePath +import Prelude hiding (takeWhile) + +type RepoName = ByteString +type RepoPath = ByteString type EclassName = String -type EclassVar = String +type EclassVar = ByteString -- | This is used for looking up what eclass variables are inherited, -- keyed by the name of the eclass. @@ -57,7 +73,7 @@ scanRepos = do let cmd = "/usr/bin/portageq" let args = ["repos_config", "/"] out <- runOrDie cmd args - case parse reposParser "scanRepos" out of + case parseOnly reposParser out of Left pe -> fail $ show pe Right nps -> do forM nps $ \(n,p) -> Repository n p <$> getEclasses p @@ -67,27 +83,29 @@ scanRepos = do reposParser :: Parser [(RepoName, RepoPath)] reposParser = choice - [ [] <$ eof + [ [] <$ endOfInput , repoName >>= repoBlock ] where -- Get the name of the repo at the top of the block repoName :: Parser RepoName - repoName - = char '[' - *> manyTill anyChar (try (char ']')) - <* endOfLine + repoName = do + _ <- char '[' + n <- takeWhile (/= fromIntegral (ord ']')) + _ <- char ']' + _ <- endOfLine + pure n -- Parse the block for location field repoBlock :: RepoName -> Parser [(RepoName, RepoPath)] repoBlock n = choice - [ try $ do - l <- string "location = " *> takeLine + [ do + l <- "location = " *> takeLine -- Found the location, skip the rest of the block skipMany miscLine *> endOfBlock insert (n,l) -- Did not find the location, keep trying - , try $ miscLine *> repoBlock n + , miscLine *> repoBlock n -- Reached the end of the block, no location field , endOfBlock *> ignore ] @@ -95,9 +113,9 @@ reposParser = miscLine :: Parser () miscLine = skipNonEmptyLine - -- A block ends with an eol or eof + -- A block either ends with an empty line or eof endOfBlock :: Parser () - endOfBlock = void endOfLine <|> eof + endOfBlock = endOfLine <|> endOfInput -- cons the repo and continue parsing insert :: (RepoName, RepoPath) -> Parser [(RepoName, RepoPath)] @@ -114,7 +132,7 @@ reposParser = -- repo. getEclasses :: RepoPath -> IO [Eclass] getEclasses repoLoc = fmap (maybe [] id) $ runMaybeT $ do - let eclassDir = repoLoc "eclass" + let eclassDir = (decodeLenient repoLoc) "eclass" -- Silently fail if the repo doesn't have an eclass dir fs <- MaybeT $ Just <$> listDirectory eclassDir <|> pure Nothing @@ -131,40 +149,57 @@ getEclasses repoLoc = fmap (maybe [] id) $ runMaybeT $ do eclassParser :: Parser [EclassVar] eclassParser = choice [ -- cons the EclassVar to the list and continue - try $ liftA2 (:) eclassVar eclassParser + liftA2 (:) eclassVar eclassParser -- or skip the line and continue , skipLine *> eclassParser -- or end the list on eof - , [] <$ eof + , [] <$ endOfInput ] where -- Scans for @ECLASS_VARIABLE comments rather than parsing the raw bash eclassVar :: Parser EclassVar - eclassVar = string "# @ECLASS_VARIABLE: " *> takeLine + eclassVar = "# @ECLASS_VARIABLE: " *> takeLine -takeLine :: Parser String -takeLine = manyTill anyChar (try endOfLine) +takeLine :: Parser ByteString +takeLine = A.takeWhile (not . isEndOfLine) <* endOfLine -- | Fails if next char is 'endOfLine' skipNonEmptyLine :: Parser () -skipNonEmptyLine = notFollowedBy endOfLine *> skipLine +skipNonEmptyLine = A.satisfy (not . isEndOfLine) *> skipLine skipLine :: Parser () -skipLine = void takeLine +skipLine = A.skipWhile (not . isEndOfLine) <* endOfLine + +parseFromFile :: Parser a -> FilePath -> IO (Either String a) +parseFromFile p = fmap (parseOnly p) . B.readFile -- | Run the command and return the full stdout string (stdin is ignored). -- -- If the command exits with a non-zero exit code, this will throw an -- error including the captured contents of stdout and stderr. -runOrDie :: FilePath -> [String] -> IO String -runOrDie cmd args = do - (ec, o, e) <- readProcessWithExitCode cmd args "" +runOrDie :: FilePath -> [String] -> IO ByteString +runOrDie cmd args = bracket acquire release $ \(_,o,e,p) -> do + ot <- B.hGetContents (fromJust o) + et <- B.hGetContents (fromJust e) + ec <- waitForProcess p case ec of - ExitSuccess -> pure o + ExitSuccess -> pure ot ExitFailure i -> fail $ unlines $ map unwords $ [ [ show cmd ] ++ map show args ++ [ "failed with exit code", show i] - , [ "stdout:" ], [ o ] - , [ "stderr:" ], [ e ] + , [ "stdout:" ], [ decodeLenient ot ] + , [ "stderr:" ], [ decodeLenient et ] ] + where + acquire = createProcess (proc cmd args) + { std_in = NoStream + , std_out = CreatePipe + , std_err = CreatePipe + } + release (i,o,e,p) = do + _ <- waitForProcess p + forM_ [i,o,e] $ mapM_ hClose + +decodeLenient :: ByteString -> String +decodeLenient = T.unpack . T.decodeUtf8With T.lenientDecode