diff options
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/Makefile.headersinst | 2 | ||||
-rw-r--r-- | scripts/Makefile.lib | 20 | ||||
-rw-r--r-- | scripts/Makefile.ubsan | 17 | ||||
-rwxr-xr-x | scripts/build-all.py | 425 | ||||
-rw-r--r-- | scripts/check-config-debug-exemptions | 58 | ||||
-rw-r--r-- | scripts/check-config-perf-exemptions | 61 | ||||
-rwxr-xr-x | scripts/check-config.py | 147 | ||||
-rwxr-xr-x | scripts/checkpatch.pl | 278 | ||||
-rw-r--r-- | scripts/dtc/libfdt/fdt.c | 14 | ||||
-rw-r--r-- | scripts/dtc/libfdt/fdt_rw.c | 2 | ||||
-rwxr-xr-x | scripts/gcc-wrapper.py | 99 | ||||
-rwxr-xr-x | scripts/setlocalversion | 23 |
12 files changed, 1125 insertions, 21 deletions
diff --git a/scripts/Makefile.headersinst b/scripts/Makefile.headersinst index 1106d6ca3a38..ed95cb4e515f 100644 --- a/scripts/Makefile.headersinst +++ b/scripts/Makefile.headersinst @@ -124,7 +124,7 @@ endif # Recursion .PHONY: $(subdirs) $(subdirs): - $(Q)$(MAKE) $(hdr-inst)=$(obj)/$@ dst=$(_dst)/$@ + $(Q)$(MAKE) $(hdr-inst)=$(obj)/$@ dst=$(_dst)/$(subst drivers/staging,usr/include/linux/staging,$@) targets := $(wildcard $(sort $(targets))) cmd_files := $(wildcard \ diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index c84080885ad4..861a2acd8cba 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -125,10 +125,16 @@ endif # ifeq ($(CONFIG_KASAN),y) _c_flags += $(if $(patsubst n%,, \ - $(KASAN_SANITIZE_$(basetarget).o)$(KASAN_SANITIZE)y), \ + $(KASAN_SANITIZE_$(basetarget).o)$(KASAN_SANITIZE)$(CONFIG_KASAN_SANITIZE_ALL)), \ $(CFLAGS_KASAN)) endif +ifeq ($(CONFIG_UBSAN),y) +_c_flags += $(if $(patsubst n%,, \ + $(UBSAN_SANITIZE_$(basetarget).o)$(UBSAN_SANITIZE)$(CONFIG_UBSAN_SANITIZE_ALL)), \ + $(CFLAGS_UBSAN)) +endif + # If building the kernel in a separate objtree expand all occurrences # of -Idir to -I$(srctree)/dir except for absolute paths (starting with '/'). @@ -294,6 +300,18 @@ $(obj)/%.dtb: $(src)/%.dts FORCE dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp) +# Helper targets for Installing DTBs into the boot directory +quiet_cmd_dtb_install = INSTALL $< + cmd_dtb_install = cp $< $(2) + +_dtbinst_pre_: + $(Q)if [ -d $(INSTALL_DTBS_PATH).old ]; then rm -rf $(INSTALL_DTBS_PATH).old; fi + $(Q)if [ -d $(INSTALL_DTBS_PATH) ]; then mv $(INSTALL_DTBS_PATH) $(INSTALL_DTBS_PATH).old; fi + $(Q)mkdir -p $(INSTALL_DTBS_PATH) + +%.dtb_dtbinst_: $(obj)/%.dtb _dtbinst_pre_ + $(call cmd,dtb_install,$(INSTALL_DTBS_PATH)) + # cat # --------------------------------------------------------------------------- # Concatentate multiple files together diff --git a/scripts/Makefile.ubsan b/scripts/Makefile.ubsan new file mode 100644 index 000000000000..8ab68679cfb5 --- /dev/null +++ b/scripts/Makefile.ubsan @@ -0,0 +1,17 @@ +ifdef CONFIG_UBSAN + CFLAGS_UBSAN += $(call cc-option, -fsanitize=shift) + CFLAGS_UBSAN += $(call cc-option, -fsanitize=integer-divide-by-zero) + CFLAGS_UBSAN += $(call cc-option, -fsanitize=unreachable) + CFLAGS_UBSAN += $(call cc-option, -fsanitize=vla-bound) + CFLAGS_UBSAN += $(call cc-option, -fsanitize=null) + CFLAGS_UBSAN += $(call cc-option, -fsanitize=signed-integer-overflow) + CFLAGS_UBSAN += $(call cc-option, -fsanitize=bounds) + CFLAGS_UBSAN += $(call cc-option, -fsanitize=object-size) + CFLAGS_UBSAN += $(call cc-option, -fsanitize=returns-nonnull-attribute) + CFLAGS_UBSAN += $(call cc-option, -fsanitize=bool) + CFLAGS_UBSAN += $(call cc-option, -fsanitize=enum) + +ifdef CONFIG_UBSAN_ALIGNMENT + CFLAGS_UBSAN += $(call cc-option, -fsanitize=alignment) +endif +endif diff --git a/scripts/build-all.py b/scripts/build-all.py new file mode 100755 index 000000000000..9e4942d21da9 --- /dev/null +++ b/scripts/build-all.py @@ -0,0 +1,425 @@ +#! /usr/bin/env python2 + +# Copyright (c) 2009-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. + +# Build the kernel for all targets using the Android build environment. + +from collections import namedtuple +import glob +from optparse import OptionParser +import os +import re +import shutil +import subprocess +import sys +import threading +import Queue + +version = 'build-all.py, version 1.99' + +build_dir = '../all-kernels' +make_command = ["vmlinux", "modules", "dtbs"] +all_options = {} +compile64 = os.environ.get('CROSS_COMPILE64') + +def error(msg): + sys.stderr.write("error: %s\n" % msg) + +def fail(msg): + """Fail with a user-printed message""" + error(msg) + sys.exit(1) + +if not os.environ.get('CROSS_COMPILE'): + fail("CROSS_COMPILE must be set in the environment") + +def check_kernel(): + """Ensure that PWD is a kernel directory""" + if (not os.path.isfile('MAINTAINERS') or + not os.path.isfile('arch/arm64/configs/msm_defconfig')): + fail("This doesn't seem to be an MSM kernel dir") + +def check_build(): + """Ensure that the build directory is present.""" + if not os.path.isdir(build_dir): + try: + os.makedirs(build_dir) + except OSError as exc: + if exc.errno == errno.EEXIST: + pass + else: + raise + +failed_targets = [] + +BuildResult = namedtuple('BuildResult', ['status', 'messages']) + +class BuildSequence(namedtuple('BuildSequence', ['log_name', 'short_name', 'steps'])): + + def set_width(self, width): + self.width = width + + def __enter__(self): + self.log = open(self.log_name, 'w') + def __exit__(self, type, value, traceback): + self.log.close() + + def run(self): + self.status = None + messages = ["Building: " + self.short_name] + def printer(line): + text = "[%-*s] %s" % (self.width, self.short_name, line) + messages.append(text) + self.log.write(text) + self.log.write('\n') + for step in self.steps: + st = step.run(printer) + if st: + self.status = BuildResult(self.short_name, messages) + break + if not self.status: + self.status = BuildResult(None, messages) + +class BuildTracker: + """Manages all of the steps necessary to perform a build. The + build consists of one or more sequences of steps. The different + sequences can be processed independently, while the steps within a + sequence must be done in order.""" + + def __init__(self, parallel_builds): + self.sequence = [] + self.lock = threading.Lock() + self.parallel_builds = parallel_builds + + def add_sequence(self, log_name, short_name, steps): + self.sequence.append(BuildSequence(log_name, short_name, steps)) + + def longest_name(self): + longest = 0 + for seq in self.sequence: + longest = max(longest, len(seq.short_name)) + return longest + + def __repr__(self): + return "BuildTracker(%s)" % self.sequence + + def run_child(self, seq): + seq.set_width(self.longest) + tok = self.build_tokens.get() + with self.lock: + print "Building:", seq.short_name + with seq: + seq.run() + self.results.put(seq.status) + self.build_tokens.put(tok) + + def run(self): + self.longest = self.longest_name() + self.results = Queue.Queue() + children = [] + errors = [] + self.build_tokens = Queue.Queue() + nthreads = self.parallel_builds + print "Building with", nthreads, "threads" + for i in range(nthreads): + self.build_tokens.put(True) + for seq in self.sequence: + child = threading.Thread(target=self.run_child, args=[seq]) + children.append(child) + child.start() + for child in children: + stats = self.results.get() + if all_options.verbose: + with self.lock: + for line in stats.messages: + print line + sys.stdout.flush() + if stats.status: + errors.append(stats.status) + for child in children: + child.join() + if errors: + fail("\n ".join(["Failed targets:"] + errors)) + +class PrintStep: + """A step that just prints a message""" + def __init__(self, message): + self.message = message + + def run(self, outp): + outp(self.message) + +class MkdirStep: + """A step that makes a directory""" + def __init__(self, direc): + self.direc = direc + + def run(self, outp): + outp("mkdir %s" % self.direc) + os.mkdir(self.direc) + +class RmtreeStep: + def __init__(self, direc): + self.direc = direc + + def run(self, outp): + outp("rmtree %s" % self.direc) + shutil.rmtree(self.direc, ignore_errors=True) + +class CopyfileStep: + def __init__(self, src, dest): + self.src = src + self.dest = dest + + def run(self, outp): + outp("cp %s %s" % (self.src, self.dest)) + shutil.copyfile(self.src, self.dest) + +class ExecStep: + def __init__(self, cmd, **kwargs): + self.cmd = cmd + self.kwargs = kwargs + + def run(self, outp): + outp("exec: %s" % (" ".join(self.cmd),)) + with open('/dev/null', 'r') as devnull: + proc = subprocess.Popen(self.cmd, stdin=devnull, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + **self.kwargs) + stdout = proc.stdout + while True: + line = stdout.readline() + if not line: + break + line = line.rstrip('\n') + outp(line) + result = proc.wait() + if result != 0: + return ('error', result) + else: + return None + +class Builder(): + + def __init__(self, name, defconfig): + self.name = name + self.defconfig = defconfig + + self.confname = self.defconfig.split('/')[-1] + + # Determine if this is a 64-bit target based on the location + # of the defconfig. + self.make_env = os.environ.copy() + if "/arm64/" in defconfig: + if compile64: + self.make_env['CROSS_COMPILE'] = compile64 + else: + fail("Attempting to build 64-bit, without setting CROSS_COMPILE64") + self.make_env['ARCH'] = 'arm64' + else: + self.make_env['ARCH'] = 'arm' + self.make_env['KCONFIG_NOTIMESTAMP'] = 'true' + self.log_name = "%s/log-%s.log" % (build_dir, self.name) + + def build(self): + steps = [] + dest_dir = os.path.join(build_dir, self.name) + log_name = "%s/log-%s.log" % (build_dir, self.name) + steps.append(PrintStep('Building %s in %s log %s' % + (self.name, dest_dir, log_name))) + if not os.path.isdir(dest_dir): + steps.append(MkdirStep(dest_dir)) + defconfig = self.defconfig + dotconfig = '%s/.config' % dest_dir + savedefconfig = '%s/defconfig' % dest_dir + + staging_dir = 'install_staging' + modi_dir = '%s' % staging_dir + hdri_dir = '%s/usr' % staging_dir + steps.append(RmtreeStep(os.path.join(dest_dir, staging_dir))) + + steps.append(ExecStep(['make', 'O=%s' % dest_dir, + self.confname], env=self.make_env)) + + if not all_options.updateconfigs: + # Build targets can be dependent upon the completion of + # previous build targets, so build them one at a time. + cmd_line = ['make', + 'INSTALL_HDR_PATH=%s' % hdri_dir, + 'INSTALL_MOD_PATH=%s' % modi_dir, + 'O=%s' % dest_dir] + build_targets = [] + for c in make_command: + if re.match(r'^-{1,2}\w', c): + cmd_line.append(c) + else: + build_targets.append(c) + for t in build_targets: + steps.append(ExecStep(cmd_line + [t], env=self.make_env)) + + # Copy the defconfig back. + if all_options.configs or all_options.updateconfigs: + steps.append(ExecStep(['make', 'O=%s' % dest_dir, + 'savedefconfig'], env=self.make_env)) + steps.append(CopyfileStep(savedefconfig, defconfig)) + + return steps + +def update_config(file, str): + print 'Updating %s with \'%s\'\n' % (file, str) + with open(file, 'a') as defconfig: + defconfig.write(str + '\n') + +def scan_configs(): + """Get the full list of defconfigs appropriate for this tree.""" + names = [] + arch_pats = ( + r'[fm]sm[0-9]*_defconfig', + r'apq*_defconfig', + r'qsd*_defconfig', + r'mpq*_defconfig', + ) + arch64_pats = ( + r'msm*_defconfig', + ) + for p in arch_pats: + for n in glob.glob('arch/arm/configs/' + p): + name = os.path.basename(n)[:-10] + names.append(Builder(name, n)) + if 'CROSS_COMPILE64' in os.environ: + for p in arch64_pats: + for n in glob.glob('arch/arm64/configs/' + p): + name = os.path.basename(n)[:-10] + "-64" + names.append(Builder(name, n)) + return names + +def build_many(targets): + print "Building %d target(s)" % len(targets) + + # To try and make up for the link phase being serial, try to do + # two full builds in parallel. Don't do too many because lots of + # parallel builds tends to use up available memory rather quickly. + parallel = 2 + if all_options.jobs and all_options.jobs > 1: + j = max(all_options.jobs / parallel, 2) + make_command.append("-j" + str(j)) + + tracker = BuildTracker(parallel) + for target in targets: + if all_options.updateconfigs: + update_config(target.defconfig, all_options.updateconfigs) + steps = target.build() + tracker.add_sequence(target.log_name, target.name, steps) + tracker.run() + +def main(): + global make_command + + check_kernel() + check_build() + + configs = scan_configs() + + usage = (""" + %prog [options] all -- Build all targets + %prog [options] target target ... -- List specific targets + %prog [options] perf -- Build all perf targets + %prog [options] noperf -- Build all non-perf targets""") + parser = OptionParser(usage=usage, version=version) + parser.add_option('--configs', action='store_true', + dest='configs', + help="Copy configs back into tree") + parser.add_option('--list', action='store_true', + dest='list', + help='List available targets') + parser.add_option('-v', '--verbose', action='store_true', + dest='verbose', + help='Output to stdout in addition to log file') + parser.add_option('--oldconfig', action='store_true', + dest='oldconfig', + help='Only process "make oldconfig"') + parser.add_option('--updateconfigs', + dest='updateconfigs', + help="Update defconfigs with provided option setting, " + "e.g. --updateconfigs=\'CONFIG_USE_THING=y\'") + parser.add_option('-j', '--jobs', type='int', dest="jobs", + help="Number of simultaneous jobs") + parser.add_option('-l', '--load-average', type='int', + dest='load_average', + help="Don't start multiple jobs unless load is below LOAD_AVERAGE") + parser.add_option('-k', '--keep-going', action='store_true', + dest='keep_going', default=False, + help="Keep building other targets if a target fails") + parser.add_option('-m', '--make-target', action='append', + help='Build the indicated make target (default: %s)' % + ' '.join(make_command)) + + (options, args) = parser.parse_args() + global all_options + all_options = options + + if options.list: + print "Available targets:" + for target in configs: + print " %s" % target.name + sys.exit(0) + + if options.oldconfig: + make_command = ["oldconfig"] + elif options.make_target: + make_command = options.make_target + + if args == ['all']: + build_many(configs) + elif args == ['perf']: + targets = [] + for t in configs: + if "perf" in t.name: + targets.append(t) + build_many(targets) + elif args == ['noperf']: + targets = [] + for t in configs: + if "perf" not in t.name: + targets.append(t) + build_many(targets) + elif len(args) > 0: + all_configs = {} + for t in configs: + all_configs[t.name] = t + targets = [] + for t in args: + if t not in all_configs: + parser.error("Target '%s' not one of %s" % (t, all_configs.keys())) + targets.append(all_configs[t]) + build_many(targets) + else: + parser.error("Must specify a target to build, or 'all'") + +if __name__ == "__main__": + main() diff --git a/scripts/check-config-debug-exemptions b/scripts/check-config-debug-exemptions new file mode 100644 index 000000000000..331a924e3612 --- /dev/null +++ b/scripts/check-config-debug-exemptions @@ -0,0 +1,58 @@ +CP15_BARRIER_EMULATION +DEVKMEM +DEVMEM +HID_A4TECH +HID_ACRUX +HID_BELKIN +HID_CHERRY +HID_CHICONY +HID_CYPRESS +HID_DRAGONRISE +HID_EMS_FF +HID_EZKEY +HID_GREENASIA +HID_GYRATION +HID_HOLTEK +HID_KENSINGTON +HID_KEYTOUCH +HID_KYE +HID_LCPOWER +HID_LOGITECH +HID_MONTEREY +HID_NTRIG +HID_ORTEK +HID_PANTHERLORD +HID_PETALYNX +HID_PICOLCD +HID_PRIMAX +HID_PRODIKEYS +HID_ROCCAT +HID_SAITEK +HID_SAMSUNG +HID_SMARTJOYPLUS +HID_SONY +HID_SPEEDLINK +HID_SUNPLUS +HID_THRUSTMASTER +HID_TIVO +HID_TOPSEED +HID_TWINHAN +HID_UCLOGIC +HID_WACOM +HID_WALTOP +HID_WIIMOTE +HID_ZEROPLUS +HID_ZYDACRON +JOYSTICK_XPAD_FF +JOYSTICK_XPAD_LEDS +KSM +MODULES +PSTORE +SETEND_EMULATION +TABLET_USB_ACECAD +TABLET_USB_AIPTEK +TABLET_USB_GTCO +TABLET_USB_HANWANG +TABLET_USB_KBTAB +USB_CONFIGFS +USB_OTG_WAKELOCK diff --git a/scripts/check-config-perf-exemptions b/scripts/check-config-perf-exemptions new file mode 100644 index 000000000000..d499f5abeb1e --- /dev/null +++ b/scripts/check-config-perf-exemptions @@ -0,0 +1,61 @@ +CGROUP_DEBUG +CP15_BARRIER_EMULATION +DEVKMEM +DEVMEM +HID_A4TECH +HID_ACRUX +HID_BELKIN +HID_CHERRY +HID_CHICONY +HID_CYPRESS +HID_DRAGONRISE +HID_EMS_FF +HID_EZKEY +HID_GREENASIA +HID_GYRATION +HID_HOLTEK +HID_KENSINGTON +HID_KEYTOUCH +HID_KYE +HID_LCPOWER +HID_LOGITECH +HID_MONTEREY +HID_NTRIG +HID_ORTEK +HID_PANTHERLORD +HID_PETALYNX +HID_PICOLCD +HID_PRIMAX +HID_PRODIKEYS +HID_ROCCAT +HID_SAITEK +HID_SAMSUNG +HID_SMARTJOYPLUS +HID_SONY +HID_SPEEDLINK +HID_SUNPLUS +HID_THRUSTMASTER +HID_TIVO +HID_TOPSEED +HID_TWINHAN +HID_UCLOGIC +HID_WACOM +HID_WALTOP +HID_WIIMOTE +HID_ZEROPLUS +HID_ZYDACRON +JOYSTICK_XPAD_FF +JOYSTICK_XPAD_LEDS +KSM +MODULES +PM_DEBUG +PSTORE +SETEND_EMULATION +SUSPEND_TIME +TABLET_USB_ACECAD +TABLET_USB_AIPTEK +TABLET_USB_GTCO +TABLET_USB_HANWANG +TABLET_USB_KBTAB +USB_CONFIGFS +USB_OTG_WAKELOCK diff --git a/scripts/check-config.py b/scripts/check-config.py new file mode 100755 index 000000000000..79c2bdf59759 --- /dev/null +++ b/scripts/check-config.py @@ -0,0 +1,147 @@ +#! /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() diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 2b3c22808c3b..17a3a6d1785c 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -11,6 +11,13 @@ use File::Basename; use Cwd 'abs_path'; use Term::ANSIColor qw(:constants); +use constant BEFORE_SHORTTEXT => 0; +use constant IN_SHORTTEXT_BLANKLINE => 1; +use constant IN_SHORTTEXT => 2; +use constant AFTER_SHORTTEXT => 3; +use constant CHECK_NEXT_SHORTTEXT => 4; +use constant SHORTTEXT_LIMIT => 75; + my $P = $0; my $D = dirname(abs_path($P)); @@ -22,6 +29,7 @@ my $quiet = 0; my $tree = 1; my $chk_signoff = 1; my $chk_patch = 1; +my $chk_author = 1; my $tst_only; my $emacs = 0; my $terse = 0; @@ -64,6 +72,7 @@ Options: -q, --quiet quiet --no-tree run without a kernel tree --no-signoff do not check for 'Signed-off-by' line + --no-author do not check for unexpected authors --patch treat FILE as patchfile (default) --emacs emacs compile window format --terse one line per report @@ -137,6 +146,7 @@ GetOptions( 'tree!' => \$tree, 'signoff!' => \$chk_signoff, 'patch!' => \$chk_patch, + 'author!' => \$chk_author, 'emacs!' => \$emacs, 'terse!' => \$terse, 'showfile!' => \$showfile, @@ -1909,6 +1919,33 @@ sub tabify { return "$leading"; } +sub cleanup_continuation_headers { + # Collapse any header-continuation lines into a single line so they + # can be parsed meaningfully, as the parser only has one line + # of context to work with. + my $again; + do { + $again = 0; + foreach my $n (0 .. scalar(@rawlines) - 2) { + if ($rawlines[$n]=~/^\s*$/) { + # A blank line means there's no more chance + # of finding headers. Shortcut to done. + return; + } + if ($rawlines[$n]=~/^[\x21-\x39\x3b-\x7e]+:/ && + $rawlines[$n+1]=~/^\s+/) { + # Continuation header. Collapse it. + my $line = splice @rawlines, $n+1, 1; + $line=~s/^\s+/ /; + $rawlines[$n] .= $line; + # We've 'destabilized' the list, so restart. + $again = 1; + last; + } + } + } while ($again); +} + sub pos_last_openparen { my ($line) = @_; @@ -1947,6 +1984,8 @@ sub process { my $prevrawline=""; my $stashline=""; my $stashrawline=""; + my $subjectline=""; + my $sublinenr=""; my $length; my $indent; @@ -2000,9 +2039,14 @@ sub process { my $setup_docs = 0; my $camelcase_file_seeded = 0; + my $shorttext = BEFORE_SHORTTEXT; + my $shorttext_exspc = 0; + my $commit_text_present = 0; sanitise_line_reset(); + cleanup_continuation_headers(); my $line; + foreach my $rawline (@rawlines) { $linenr++; $line = $rawline; @@ -2184,13 +2228,115 @@ sub process { } next; } - $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); my $hereline = "$here\n$rawline\n"; my $herecurr = "$here\n$rawline\n"; my $hereprev = "$here\n$prevrawline\n$rawline\n"; + if ($shorttext != AFTER_SHORTTEXT) { + if ($shorttext == IN_SHORTTEXT_BLANKLINE && $line=~/\S/) { + # the subject line was just processed, + # a blank line must be next + WARN("NONBLANK_AFTER_SUMMARY", + "non-blank line after summary line\n" . $herecurr); + $shorttext = IN_SHORTTEXT; + # this non-blank line may or may not be commit text - + # a warning has been generated so assume it is commit + # text and move on + $commit_text_present = 1; + # fall through and treat this line as IN_SHORTTEXT + } + if ($shorttext == IN_SHORTTEXT) { + if ($line=~/^---/ || $line=~/^diff.*/) { + if ($commit_text_present == 0) { + WARN("NO_COMMIT_TEXT", + "please add commit text explaining " . + "*why* the change is needed\n" . + $herecurr); + } + $shorttext = AFTER_SHORTTEXT; + } elsif (length($line) > (SHORTTEXT_LIMIT + + $shorttext_exspc) + && $line !~ /^:([0-7]{6}\s){2} + ([[:xdigit:]]+\.* + \s){2}\w+\s\w+/xms) { + WARN("LONG_COMMIT_TEXT", + "commit text line over " . + SHORTTEXT_LIMIT . + " characters\n" . $herecurr); + } elsif ($line=~/^\s*change-id:/i || + $line=~/^\s*signed-off-by:/i || + $line=~/^\s*crs-fixed:/i || + $line=~/^\s*acked-by:/i) { + # this is a tag, there must be commit + # text by now + if ($commit_text_present == 0) { + WARN("NO_COMMIT_TEXT", + "please add commit text explaining " . + "*why* the change is needed\n" . + $herecurr); + # prevent duplicate warnings + $commit_text_present = 1; + } + } elsif ($line=~/\S/) { + $commit_text_present = 1; + } + } elsif ($shorttext == IN_SHORTTEXT_BLANKLINE) { + # case of non-blank line in this state handled above + $shorttext = IN_SHORTTEXT; + } elsif ($shorttext == CHECK_NEXT_SHORTTEXT) { +# The Subject line doesn't have to be the last header in the patch. +# Avoid moving to the IN_SHORTTEXT state until clear of all headers. +# Per RFC5322, continuation lines must be folded, so any left-justified +# text which looks like a header is definitely a header. + if ($line!~/^[\x21-\x39\x3b-\x7e]+:/) { + $shorttext = IN_SHORTTEXT; + # Check for Subject line followed by a blank line. + if (length($line) != 0) { + WARN("NONBLANK_AFTER_SUMMARY", + "non-blank line after " . + "summary line\n" . + $sublinenr . $here . + "\n" . $subjectline . + "\n" . $line . "\n"); + # this non-blank line may or may not + # be commit text - a warning has been + # generated so assume it is commit + # text and move on + $commit_text_present = 1; + } + } + # The next two cases are BEFORE_SHORTTEXT. + } elsif ($line=~/^Subject: \[[^\]]*\] (.*)/) { + # This is the subject line. Go to + # CHECK_NEXT_SHORTTEXT to wait for the commit + # text to show up. + $shorttext = CHECK_NEXT_SHORTTEXT; + $subjectline = $line; + $sublinenr = "#$linenr & "; +# Check for Subject line less than line limit + if (length($1) > SHORTTEXT_LIMIT && !($1 =~ m/Revert\ \"/)) { + WARN("LONG_SUMMARY_LINE", + "summary line over " . + SHORTTEXT_LIMIT . + " characters\n" . $herecurr); + } + } elsif ($line=~/^ (.*)/) { + # Indented format, this must be the summary + # line (i.e. git show). There will be no more + # headers so we are now in the shorttext. + $shorttext = IN_SHORTTEXT_BLANKLINE; + $shorttext_exspc = 4; + if (length($1) > SHORTTEXT_LIMIT && !($1 =~ m/Revert\ \"/)) { + WARN("LONG_SUMMARY_LINE", + "summary line over " . + SHORTTEXT_LIMIT . + " characters\n" . $herecurr); + } + } + } + $cnt_lines++ if ($realcnt != 0); # Check if the commit log has what seems like a diff which can confuse patch @@ -2283,6 +2429,10 @@ sub process { "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr); } } + if ($chk_author && $line =~ /^\s*signed-off-by:.*(quicinc|qualcomm)\.com/i) { + WARN("BAD_SIGN_OFF", + "invalid Signed-off-by identity\n" . $line ); + } # Check for duplicate signatures my $sig_nospace = $line; @@ -2309,12 +2459,6 @@ sub process { "The 'stable' address should be 'stable\@vger.kernel.org'\n" . $herecurr); } -# Check for unwanted Gerrit info - if ($in_commit_log && $line =~ /^\s*change-id:/i) { - ERROR("GERRIT_CHANGE_ID", - "Remove Gerrit Change-Id's before submitting upstream.\n" . $herecurr); - } - # Check if the commit log is in a possible stack dump if ($in_commit_log && !$commit_log_possible_stack_dump && ($line =~ /^\s*(?:WARNING:|BUG:)/ || @@ -2413,6 +2557,11 @@ sub process { "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); } +#check the patch for invalid author credentials + if ($chk_author && $line =~ /^From:.*(quicinc|qualcomm)\.com/) { + WARN("BAD_AUTHOR", "invalid author identity\n" . $line ); + } + # Check for wrappage within a valid hunk of the file if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { ERROR("CORRUPTED_PATCH", @@ -2639,8 +2788,7 @@ sub process { # # if LONG_LINE is ignored, the other 2 types are also ignored # - - if ($line =~ /^\+/ && $length > $max_line_length) { + if ($line =~ /^\+/ && $length > $max_line_length && $realfile ne "scripts/checkpatch.pl") { my $msg_type = "LONG_LINE"; # Check the allowed long line types first @@ -4134,7 +4282,7 @@ sub process { # check spacing on parentheses if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && - $line !~ /for\s*\(\s+;/) { + $line !~ /for\s*\(\s+;/ && $line !~ /^\+\s*[A-Z_][A-Z\d_]*\(\s*\d+(\,.*)?\)\,?$/) { if (ERROR("SPACING", "space prohibited after that open parenthesis '('\n" . $herecurr) && $fix) { @@ -4505,7 +4653,7 @@ sub process { if ($realfile !~ m@/vmlinux.lds.h$@ && $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { my $ln = $linenr; - my $cnt = $realcnt; + my $cnt = $realcnt - 1; my ($off, $dstat, $dcond, $rest); my $ctx = ''; my $has_flow_statement = 0; @@ -4532,6 +4680,12 @@ sub process { { } + # Extremely long macros may fall off the end of the + # available context without closing. Give a dangling + # backslash the benefit of the doubt and allow it + # to gobble any hanging open-parens. + $dstat =~ s/\(.+\\$/1/; + # Flatten any obvious string concatentation. while ($dstat =~ s/($String)\s*$Ident/$1/ || $dstat =~ s/$Ident\s*($String)/$1/) @@ -4544,6 +4698,7 @@ sub process { MODULE_PARM_DESC| DECLARE_PER_CPU| DEFINE_PER_CPU| + CLK_[A-Z\d_]+| __typeof__\(| union| struct| @@ -4894,11 +5049,94 @@ sub process { "Avoid line continuations in quoted strings\n" . $herecurr); } +# sys_open/read/write/close are not allowed in the kernel + if ($line =~ /\b(sys_(?:open|read|write|close))\b/) { + ERROR("FILE_OPS", + "$1 is inappropriate in kernel code.\n" . + $herecurr); + } + +# filp_open is a backdoor for sys_open + if ($line =~ /\b(filp_open)\b/) { + ERROR("FILE_OPS", + "$1 is inappropriate in kernel code.\n" . + $herecurr); + } + +# read[bwl] & write[bwl] use too many barriers, use the _relaxed variants + if ($line =~ /\b((?:read|write)[bwl])\b/) { + ERROR("NON_RELAXED_IO", + "Use of $1 is deprecated: use $1_relaxed\n\t" . + "with appropriate memory barriers instead.\n" . + $herecurr); + } + +# likewise, in/out[bwl] should be __raw_read/write[bwl]... + if ($line =~ /\b((in|out)([bwl]))\b/) { + my ($all, $pref, $suf) = ($1, $2, $3); + $pref =~ s/in/read/; + $pref =~ s/out/write/; + ERROR("NON_RELAXED_IO", + "Use of $all is deprecated: use " . + "__raw_$pref$suf\n\t" . + "with appropriate memory barriers instead.\n" . + $herecurr); + } + +# dsb is too ARMish, and should usually be mb. + if ($line =~ /[^-_>*\.]\bdsb\b[^-_\.;]/) { + WARN("ARM_BARRIER", + "Use of dsb is discouranged: prefer mb.\n" . + $herecurr); + } + +# MSM - check if a non board-gpiomux file has any gpiomux declarations + if ($realfile =~ /\/mach-msm\/board-[0-9]+/ && + $realfile !~ /camera/ && $realfile !~ /gpiomux/ && + $line =~ /\s*struct msm_gpiomux_config\s*/ ) { + WARN("GPIOMUX_IN_BOARD", + "Non gpiomux board file cannot have a gpiomux config declarations. Please declare gpiomux configs in board-*-gpiomux.c file.\n" . $herecurr); + } + +# MSM - check if vreg_xxx function are used + if ($line =~ /\b(vreg_(get|put|set_level|enable|disable))\b/) { + WARN("DEPRECATED_VREG_APIS", + "Use of $1 API is deprecated: " . + "use regulator APIs\n" . $herecurr); + } + +# unbounded string functions are overflow risks + my %str_fns = ( + "sprintf" => "snprintf", + "strcpy" => "strlcpy", + "strncpy" => "strlcpy", + "strcat" => "strlcat", + "strncat" => "strlcat", + "vsprintf" => "vsnprintf", + "strchr" => "strnchr", + "strstr" => "strnstr", + ); + foreach my $k (keys %str_fns) { + if ($line =~ /\b$k\b/) { + ERROR("UNBOUNDED_STRING_FNS", + "Use of $k is deprecated: " . + "use $str_fns{$k} instead.\n" . + $herecurr); + } + } + # warn about #if 0 if ($line =~ /^.\s*\#\s*if\s+0\b/) { - CHK("REDUNDANT_CODE", - "if this code is redundant consider removing it\n" . - $herecurr); + WARN("IF_0", + "if this code is redundant consider removing it\n" + . $herecurr); + } + +# warn about #if 1 + if ($line =~ /^.\s*\#\s*if\s+1\b/) { + WARN("IF_1", + "if this code is required consider removing" + . " #if 1\n" . $herecurr); } # check for needless "if (<foo>) fn(<foo>)" uses @@ -5088,6 +5326,12 @@ sub process { "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr); } +# check the patch for use of mdelay + if ($line =~ /\bmdelay\s*\(/) { + WARN("MDELAY", + "use of mdelay() found: msleep() is the preferred API.\n" . $herecurr ); + } + # warn about #ifdefs in C files # if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { # print "#ifdef in C files should be avoided\n"; @@ -5574,6 +5818,12 @@ sub process { "switch default: should use break\n" . $herectx); } +# check for return codes on error paths + if ($line =~ /\breturn\s+-\d+/) { + ERROR("NO_ERROR_CODE", + "illegal return value, please use an error code\n" . $herecurr); + } + # check for gcc specific __FUNCTION__ if ($line =~ /\b__FUNCTION__\b/) { if (WARN("USE_FUNC", diff --git a/scripts/dtc/libfdt/fdt.c b/scripts/dtc/libfdt/fdt.c index 2ce6a44179de..d96ed24509c7 100644 --- a/scripts/dtc/libfdt/fdt.c +++ b/scripts/dtc/libfdt/fdt.c @@ -71,6 +71,20 @@ int fdt_check_header(const void *fdt) return -FDT_ERR_BADMAGIC; } + if (fdt_off_dt_struct(fdt) > (UINT_MAX - fdt_size_dt_struct(fdt))) + return FDT_ERR_BADOFFSET; + + if (fdt_off_dt_strings(fdt) > (UINT_MAX - fdt_size_dt_strings(fdt))) + return FDT_ERR_BADOFFSET; + + if ((fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt)) + > fdt_totalsize(fdt)) + return FDT_ERR_BADOFFSET; + + if ((fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)) + > fdt_totalsize(fdt)) + return FDT_ERR_BADOFFSET; + return 0; } diff --git a/scripts/dtc/libfdt/fdt_rw.c b/scripts/dtc/libfdt/fdt_rw.c index 70adec6c371b..4463c10f59e3 100644 --- a/scripts/dtc/libfdt/fdt_rw.c +++ b/scripts/dtc/libfdt/fdt_rw.c @@ -394,7 +394,7 @@ int fdt_del_node(void *fdt, int nodeoffset) static void _fdt_packblocks(const char *old, char *new, int mem_rsv_size, int struct_size) { - int mem_rsv_off, struct_off, strings_off; + uint32_t mem_rsv_off, struct_off, strings_off; mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8); struct_off = mem_rsv_off + mem_rsv_size; diff --git a/scripts/gcc-wrapper.py b/scripts/gcc-wrapper.py new file mode 100755 index 000000000000..8a0e0af1c39a --- /dev/null +++ b/scripts/gcc-wrapper.py @@ -0,0 +1,99 @@ +#! /usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Copyright (c) 2011-2016, 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. + +# Invoke gcc, looking for warnings, and causing a failure if there are +# non-whitelisted warnings. + +import errno +import re +import os +import sys +import subprocess + +# Note that gcc uses unicode, which may depend on the locale. TODO: +# force LANG to be set to en_US.UTF-8 to get consistent warnings. + +allowed_warnings = set([ + "core.c:144", + "inet_connection_sock.c:430", + "inet_connection_sock.c:467", + "inet6_connection_sock.c:89", + ]) + +# Capture the name of the object file, can find it. +ofile = None + +warning_re = re.compile(r'''(.*/|)([^/]+\.[a-z]+:\d+):(\d+:)? warning:''') +def interpret_warning(line): + """Decode the message from gcc. The messages we care about have a filename, and a warning""" + line = line.rstrip('\n') + m = warning_re.match(line) + if m and m.group(2) not in allowed_warnings: + print "error, forbidden warning:", m.group(2) + + # If there is a warning, remove any object if it exists. + if ofile: + try: + os.remove(ofile) + except OSError: + pass + sys.exit(1) + +def run_gcc(): + args = sys.argv[1:] + # Look for -o + try: + i = args.index('-o') + global ofile + ofile = args[i+1] + except (ValueError, IndexError): + pass + + compiler = sys.argv[0] + + try: + proc = subprocess.Popen(args, stderr=subprocess.PIPE) + for line in proc.stderr: + print line, + interpret_warning(line) + + result = proc.wait() + except OSError as e: + result = e.errno + if result == errno.ENOENT: + print args[0] + ':',e.strerror + print 'Is your PATH set correctly?' + else: + print ' '.join(args), str(e) + + return result + +if __name__ == '__main__': + status = run_gcc() + sys.exit(status) diff --git a/scripts/setlocalversion b/scripts/setlocalversion index 63d91e22ed7c..43f29132a28c 100755 --- a/scripts/setlocalversion +++ b/scripts/setlocalversion @@ -48,7 +48,20 @@ scm_version() # If we are at a tagged commit (like "v2.6.30-rc6"), we ignore # it, because this version is defined in the top level Makefile. - if [ -z "`git describe --exact-match 2>/dev/null`" ]; then + if atag="`git describe --exact-match --abbrev=0 2>/dev/null`"; then + # Make sure we're at the tag that matches the Makefile. + # If not place the hash of the tag as well for + # v2.6.30-rc5-g314aef + if [ "x$atag" != "x$VERSION" ]; then + # If only the short version is requested, + # don't bother running further git commands + if $short; then + echo "+" + return + fi + printf '%s%s' -g "`git show-ref -s --abbrev --tags $atag 2>/dev/null`" + fi + else # If only the short version is requested, don't bother # running further git commands @@ -57,10 +70,12 @@ scm_version() return fi # If we are past a tagged commit (like - # "v2.6.30-rc5-302-g72357d5"), we pretty print it. + # "v2.6.30-rc5-302-g72357d5"), we pretty print it and + # include the hash of any new tag on top. if atag="`git describe 2>/dev/null`"; then - echo "$atag" | awk -F- '{printf("-%05d-%s", $(NF-1),$(NF))}' - + tag="`git describe --abbrev=0 2>/dev/null`" + commit="`echo "$atag" | awk -F- '{printf("-%05d-%s", $(NF-1),$(NF))}'`" + printf '%s%s%s' -g "`git show-ref -s --abbrev --tags $tag 2>/dev/null`" $commit # If we don't have a tag at all we print -g{commitish}. else printf '%s%s' -g $head |