e7e7a0afca502afcb20d7c0b303620ebecaf4138
[gem5.git] / site_scons / gem5_scons / __init__.py
1 # Copyright (c) 2013, 2015-2017 ARM Limited
2 # All rights reserved.
3 #
4 # The license below extends only to copyright in the software and shall
5 # not be construed as granting a license to any other intellectual
6 # property including but not limited to intellectual property relating
7 # to a hardware implementation of the functionality of the software
8 # licensed hereunder. You may use the software subject to the license
9 # terms below provided that you ensure that this notice is replicated
10 # unmodified and in its entirety in all distributions of the software,
11 # modified or unmodified, in source code or in binary form.
12 #
13 # Copyright (c) 2011 Advanced Micro Devices, Inc.
14 # Copyright (c) 2009 The Hewlett-Packard Development Company
15 # Copyright (c) 2004-2005 The Regents of The University of Michigan
16 # All rights reserved.
17 #
18 # Redistribution and use in source and binary forms, with or without
19 # modification, are permitted provided that the following conditions are
20 # met: redistributions of source code must retain the above copyright
21 # notice, this list of conditions and the following disclaimer;
22 # redistributions in binary form must reproduce the above copyright
23 # notice, this list of conditions and the following disclaimer in the
24 # documentation and/or other materials provided with the distribution;
25 # neither the name of the copyright holders nor the names of its
26 # contributors may be used to endorse or promote products derived from
27 # this software without specific prior written permission.
28 #
29 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
41 from __future__ import print_function
42
43 import os
44 import textwrap
45
46 from gem5_scons.util import get_termcap
47 import SCons.Script
48
49 termcap = get_termcap()
50
51 def strip_build_path(path, env):
52 path = str(path)
53 build_base = 'build/'
54 variant_base = env['BUILDROOT'] + os.path.sep
55 if path.startswith(variant_base):
56 path = path[len(variant_base):]
57 elif path.startswith(build_base):
58 path = path[len(build_base):]
59 return path
60
61 # Generate a string of the form:
62 # common/path/prefix/src1, src2 -> tgt1, tgt2
63 # to print while building.
64 class Transform(object):
65 # all specific color settings should be here and nowhere else
66 tool_color = termcap.Normal
67 pfx_color = termcap.Yellow
68 srcs_color = termcap.Yellow + termcap.Bold
69 arrow_color = termcap.Blue + termcap.Bold
70 tgts_color = termcap.Yellow + termcap.Bold
71
72 def __init__(self, tool, max_sources=99):
73 self.format = self.tool_color + (" [%8s] " % tool) \
74 + self.pfx_color + "%s" \
75 + self.srcs_color + "%s" \
76 + self.arrow_color + " -> " \
77 + self.tgts_color + "%s" \
78 + termcap.Normal
79 self.max_sources = max_sources
80
81 def __call__(self, target, source, env, for_signature=None):
82 # truncate source list according to max_sources param
83 source = source[0:self.max_sources]
84 def strip(f):
85 return strip_build_path(str(f), env)
86 if len(source) > 0:
87 srcs = list(map(strip, source))
88 else:
89 srcs = ['']
90 tgts = list(map(strip, target))
91 # surprisingly, os.path.commonprefix is a dumb char-by-char string
92 # operation that has nothing to do with paths.
93 com_pfx = os.path.commonprefix(srcs + tgts)
94 com_pfx_len = len(com_pfx)
95 if com_pfx:
96 # do some cleanup and sanity checking on common prefix
97 if com_pfx[-1] == ".":
98 # prefix matches all but file extension: ok
99 # back up one to change 'foo.cc -> o' to 'foo.cc -> .o'
100 com_pfx = com_pfx[0:-1]
101 elif com_pfx[-1] == "/":
102 # common prefix is directory path: OK
103 pass
104 else:
105 src0_len = len(srcs[0])
106 tgt0_len = len(tgts[0])
107 if src0_len == com_pfx_len:
108 # source is a substring of target, OK
109 pass
110 elif tgt0_len == com_pfx_len:
111 # target is a substring of source, need to back up to
112 # avoid empty string on RHS of arrow
113 sep_idx = com_pfx.rfind(".")
114 if sep_idx != -1:
115 com_pfx = com_pfx[0:sep_idx]
116 else:
117 com_pfx = ''
118 elif src0_len > com_pfx_len and srcs[0][com_pfx_len] == ".":
119 # still splitting at file extension: ok
120 pass
121 else:
122 # probably a fluke; ignore it
123 com_pfx = ''
124 # recalculate length in case com_pfx was modified
125 com_pfx_len = len(com_pfx)
126 def fmt(files):
127 f = list(map(lambda s: s[com_pfx_len:], files))
128 return ', '.join(f)
129 return self.format % (com_pfx, fmt(srcs), fmt(tgts))
130
131 # The width warning and error messages should be wrapped at.
132 text_width = None
133
134 # This should work in python 3.3 and above.
135 if text_width is None:
136 try:
137 import shutil
138 text_width = shutil.get_terminal_size().columns
139 except:
140 pass
141
142 # This should work if the curses python module is installed.
143 if text_width is None:
144 try:
145 import curses
146 try:
147 _, text_width = curses.initscr().getmaxyx()
148 finally:
149 curses.endwin()
150 except:
151 pass
152
153 # If all else fails, default to 80 columns.
154 if text_width is None:
155 text_width = 80
156
157 def print_message(prefix, color, message, **kwargs):
158 # Precompute some useful values.
159 prefix_len = len(prefix)
160 wrap_width = text_width - prefix_len
161 padding = ' ' * prefix_len
162
163 # First split on newlines.
164 lines = message.split('\n')
165 # Then wrap each line to the required width.
166 wrapped_lines = []
167 for line in lines:
168 wrapped_lines.extend(textwrap.wrap(line, wrap_width))
169 # Finally add the prefix and padding on extra lines, and glue it all back
170 # together.
171 message = prefix + ('\n' + padding).join(wrapped_lines)
172 # Print the message in bold in the requested color.
173 print(color + termcap.Bold + message + termcap.Normal, **kwargs)
174
175 def warning(*args, **kwargs):
176 message = ' '.join(args)
177 print_message('Warning: ', termcap.Yellow, message, **kwargs)
178
179 def error(*args, **kwargs):
180 message = ' '.join(args)
181 print_message('Error: ', termcap.Red, message, **kwargs)
182 SCons.Script.Exit(1)
183
184 __all__ = ['Transform', 'warning', 'error']