| #! /usr/bin/env python |
| |
| # Copyright (c) 2015, The Linux Foundation. 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 The Linux Foundation 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, FITNESS FOR A PARTICULAR PURPOSE AND |
| # NON-INFRINGEMENT 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. |
| |
| """ |
| Android kernel configuration validator. |
| |
| The Android kernel reference trees contain some config stubs of |
| configuration options that are required for Android to function |
| correctly, and additional ones that are recommended. |
| |
| This script can help compare these base configs with the ".config" |
| output of the compiler to determine if the proper configs are defined. |
| """ |
| |
| from collections import namedtuple |
| from optparse import OptionParser |
| import re |
| import sys |
| |
| version = "check-config.py, version 0.0.1" |
| |
| req_re = re.compile(r'''^CONFIG_(.*)=(.*)$''') |
| forb_re = re.compile(r'''^# CONFIG_(.*) is not set$''') |
| comment_re = re.compile(r'''^(#.*|)$''') |
| |
| Enabled = namedtuple('Enabled', ['name', 'value']) |
| Disabled = namedtuple('Disabled', ['name']) |
| |
| def walk_config(name): |
| with open(name, 'r') as fd: |
| for line in fd: |
| line = line.rstrip() |
| m = req_re.match(line) |
| if m: |
| yield Enabled(m.group(1), m.group(2)) |
| continue |
| |
| m = forb_re.match(line) |
| if m: |
| yield Disabled(m.group(1)) |
| continue |
| |
| m = comment_re.match(line) |
| if m: |
| continue |
| |
| print "WARNING: Unknown .config line: ", line |
| |
| class Checker(): |
| def __init__(self): |
| self.required = {} |
| self.exempted = set() |
| self.forbidden = set() |
| |
| def add_required(self, fname): |
| for ent in walk_config(fname): |
| if type(ent) is Enabled: |
| self.required[ent.name] = ent.value |
| elif type(ent) is Disabled: |
| if ent.name in self.required: |
| del self.required[ent.name] |
| self.forbidden.add(ent.name) |
| |
| def add_exempted(self, fname): |
| with open(fname, 'r') as fd: |
| for line in fd: |
| line = line.rstrip() |
| self.exempted.add(line) |
| |
| def check(self, path): |
| failure = False |
| |
| # Don't run this for mdm targets |
| if re.search('mdm', path): |
| print "Not applicable to mdm targets... bypassing" |
| else: |
| for ent in walk_config(path): |
| # Go to the next iteration if this config is exempt |
| if ent.name in self.exempted: |
| continue |
| |
| if type(ent) is Enabled: |
| if ent.name in self.forbidden: |
| print "error: Config should not be present: %s" %ent.name |
| failure = True |
| |
| if ent.name in self.required and ent.value != self.required[ent.name]: |
| print "error: Config has wrong value: %s %s expecting: %s" \ |
| %(ent.name, ent.value, self.required[ent.name]) |
| failure = True |
| |
| elif type(ent) is Disabled: |
| if ent.name in self.required: |
| print "error: Config should be present, but is disabled: %s" %ent.name |
| failure = True |
| |
| if failure: |
| sys.exit(1) |
| |
| def main(): |
| usage = """%prog [options] path/to/.config""" |
| parser = OptionParser(usage=usage, version=version) |
| parser.add_option('-r', '--required', dest="required", |
| action="append") |
| parser.add_option('-e', '--exempted', dest="exempted", |
| action="append") |
| (options, args) = parser.parse_args() |
| if len(args) != 1: |
| parser.error("Expecting a single path argument to .config") |
| elif options.required is None or options.exempted is None: |
| parser.error("Expecting a file containing required configurations") |
| |
| ch = Checker() |
| for r in options.required: |
| ch.add_required(r) |
| for e in options.exempted: |
| ch.add_exempted(e) |
| |
| ch.check(args[0]) |
| |
| if __name__ == '__main__': |
| main() |