# Copyright (c) 2013, 2015-2017 ARM Limited # All rights reserved. # # The license below extends only to copyright in the software and shall # not be construed as granting a license to any other intellectual # property including but not limited to intellectual property relating # to a hardware implementation of the functionality of the software # licensed hereunder. You may use the software subject to the license # terms below provided that you ensure that this notice is replicated # unmodified and in its entirety in all distributions of the software, # modified or unmodified, in source code or in binary form. # # Copyright (c) 2011 Advanced Micro Devices, Inc. # Copyright (c) 2009 The Hewlett-Packard Development Company # Copyright (c) 2004-2005 The Regents of The University of Michigan # 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 copyright holders 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. import os import pickle import sys import tempfile import textwrap from gem5_scons.util import get_termcap from gem5_scons.configure import Configure from gem5_scons.defaults import EnvDefaults import SCons.Node.Python import SCons.Script termcap = get_termcap() def strip_build_path(path, env): path = str(path) build_base = 'build/' variant_base = env['BUILDROOT'] + os.path.sep if path.startswith(variant_base): path = path[len(variant_base):] elif path.startswith(build_base): path = path[len(build_base):] return path def TempFileSpawn(scons_env): old_pspawn = scons_env['PSPAWN'] old_spawn = scons_env['SPAWN'] def wrapper(old, sh, esc, cmd, sh_args, *py_args): with tempfile.NamedTemporaryFile() as temp: temp.write(' '.join(sh_args).encode()) temp.flush() sh_args = [sh, esc(temp.name)] return old(sh, esc, sh, sh_args, *py_args) def new_pspawn(sh, esc, cmd, args, sh_env, stdout, stderr): return wrapper(old_pspawn, sh, esc, cmd, args, sh_env, stdout, stderr) def new_spawn(sh, esc, cmd, args, sh_env): return wrapper(old_spawn, sh, esc, cmd, args, sh_env) scons_env['PSPAWN'] = new_pspawn scons_env['SPAWN'] = new_spawn # Generate a string of the form: # common/path/prefix/src1, src2 -> tgt1, tgt2 # to print while building. class Transform(object): # all specific color settings should be here and nowhere else tool_color = termcap.Normal pfx_color = termcap.Yellow srcs_color = termcap.Yellow + termcap.Bold arrow_color = termcap.Blue + termcap.Bold tgts_color = termcap.Yellow + termcap.Bold def __init__(self, tool, max_sources=99): self.format = self.tool_color + (" [%8s] " % tool) \ + self.pfx_color + "%s" \ + self.srcs_color + "%s" \ + self.arrow_color + " -> " \ + self.tgts_color + "%s" \ + termcap.Normal self.max_sources = max_sources def __call__(self, target, source, env, for_signature=None): # truncate source list according to max_sources param source = source[0:self.max_sources] def strip(f): return strip_build_path(str(f), env) if len(source) > 0: srcs = list(map(strip, source)) else: srcs = [''] tgts = list(map(strip, target)) # surprisingly, os.path.commonprefix is a dumb char-by-char string # operation that has nothing to do with paths. com_pfx = os.path.commonprefix(srcs + tgts) com_pfx_len = len(com_pfx) if com_pfx: # do some cleanup and sanity checking on common prefix if com_pfx[-1] == ".": # prefix matches all but file extension: ok # back up one to change 'foo.cc -> o' to 'foo.cc -> .o' com_pfx = com_pfx[0:-1] elif com_pfx[-1] == "/": # common prefix is directory path: OK pass else: src0_len = len(srcs[0]) tgt0_len = len(tgts[0]) if src0_len == com_pfx_len: # source is a substring of target, OK pass elif tgt0_len == com_pfx_len: # target is a substring of source, need to back up to # avoid empty string on RHS of arrow sep_idx = com_pfx.rfind(".") if sep_idx != -1: com_pfx = com_pfx[0:sep_idx] else: com_pfx = '' elif src0_len > com_pfx_len and srcs[0][com_pfx_len] == ".": # still splitting at file extension: ok pass else: # probably a fluke; ignore it com_pfx = '' # recalculate length in case com_pfx was modified com_pfx_len = len(com_pfx) def fmt(files): f = list(map(lambda s: s[com_pfx_len:], files)) return ', '.join(f) return self.format % (com_pfx, fmt(srcs), fmt(tgts)) # The width warning and error messages should be wrapped at. text_width = None # If stdout is not attached to a terminal, default to 80 columns. if not sys.stdout.isatty(): text_width = 80 # This should work in python 3.3 and above. if text_width is None: try: import shutil text_width = shutil.get_terminal_size().columns except: pass # This should work if the curses python module is installed. if text_width is None: try: import curses try: _, text_width = curses.initscr().getmaxyx() finally: curses.endwin() except: pass # If all else fails, default to 80 columns. if text_width is None: text_width = 80 def print_message(prefix, color, message, **kwargs): prefix_len = len(prefix) if text_width > prefix_len: wrap_width = text_width - prefix_len padding = ' ' * prefix_len # First split on newlines. lines = message.split('\n') # Then wrap each line to the required width. wrapped_lines = [] for line in lines: wrapped_lines.extend(textwrap.wrap(line, wrap_width)) # Finally add the prefix and padding on extra lines, and glue it all # back together. message = prefix + ('\n' + padding).join(wrapped_lines) else: # We have very small terminal, indent formatting doesn't help. message = prefix + message # Add in terminal escape sequences. message = color + termcap.Bold + message + termcap.Normal # Actually print the message. print(message, **kwargs) return message all_warnings = [] def summarize_warnings(): if not all_warnings: return print(termcap.Yellow + termcap.Bold + '*** Summary of Warnings ***' + termcap.Normal) list(map(print, all_warnings)) def warning(*args, **kwargs): message = ' '.join(args) printed = print_message('Warning: ', termcap.Yellow, message, **kwargs) all_warnings.append(printed) def error(*args, **kwargs): message = ' '.join(args) print_message('Error: ', termcap.Red, message, **kwargs) SCons.Script.Exit(1) def parse_build_path(target): path_dirs = target.split('/') # Pop off the target file. path_dirs.pop() # Search backwards for the "build" directory. Whatever was just before it # was the name of the variant. variant_dir = path_dirs.pop() while path_dirs and path_dirs[-1] != 'build': variant_dir = path_dirs.pop() if not path_dirs: error("No non-leaf 'build' dir found on target path.", t) return os.path.join('/', *path_dirs), variant_dir # The MakeAction wrapper, and a SCons tool to set up the *COMSTR variables. if SCons.Script.GetOption('verbose'): def MakeAction(action, string, *args, **kwargs): return SCons.Script.Action(action, *args, **kwargs) def MakeActionTool(env): pass else: MakeAction = SCons.Script.Action def MakeActionTool(env): env['CCCOMSTR'] = Transform("CC") env['CXXCOMSTR'] = Transform("CXX") env['ASCOMSTR'] = Transform("AS") env['ARCOMSTR'] = Transform("AR", 0) env['LINKCOMSTR'] = Transform("LINK", 0) env['SHLINKCOMSTR'] = Transform("SHLINK", 0) env['RANLIBCOMSTR'] = Transform("RANLIB", 0) env['M4COMSTR'] = Transform("M4") env['SHCCCOMSTR'] = Transform("SHCC") env['SHCXXCOMSTR'] = Transform("SHCXX") def ToValue(obj): return SCons.Node.Python.Value(pickle.dumps(obj)) def FromValue(node): return pickle.loads(node.read()) __all__ = ['Configure', 'EnvDefaults', 'Transform', 'warning', 'error', 'MakeAction', 'MakeActionTool', 'ToValue', 'FromValue']