diff --git a/ShellCheck.cabal b/ShellCheck.cabal index f09521ff4..f9c8c76aa 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 000000000..58258c9ac --- /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 000000000..7351698df --- /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 9675e79ec..cd7b22a6f 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 64fa7625f..6cf48cb1e 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 dbad0f570..ad7f24c38 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 ca928fd7a..8105b65a1 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 3b4f95782..f9ac65fb7 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 c79f90fbb..5cd6ae858 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 691836f42..03911e079 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 550ff87a3..6a7ab6cf0 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 75285592a..60a9b9495 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 000000000..e5558e7c2 --- /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"]) + ]