1 # Copyright (c) 2017 Mark D. Hill and David A. Wood
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met: redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer;
8 # redistributions in binary form must reproduce the above copyright
9 # notice, this list of conditions and the following disclaimer in the
10 # documentation and/or other materials provided with the distribution;
11 # neither the name of the copyright holders nor the names of its
12 # contributors may be used to endorse or promote products derived from
13 # this software without specific prior written permission.
15 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 # Authors: Sean Wilson
33 from testlib
.fixture
import Fixture
, globalfixture
34 from testlib
.config
import config
, constants
35 from testlib
.helper
import log_call
, cacheresult
, joinpath
, absdirpath
36 import testlib
.log
as log
39 class VariableFixture(Fixture
):
40 def __init__(self
, value
=None, name
=None):
41 super(VariableFixture
, self
).__init
__(name
=name
)
45 class TempdirFixture(Fixture
):
48 super(TempdirFixture
, self
).__init
__(
49 name
=constants
.tempdir_fixture_name
)
51 def setup(self
, testitem
):
52 self
.path
= tempfile
.mkdtemp(prefix
='gem5out')
54 def teardown(self
, testitem
):
55 if self
.path
is not None:
56 shutil
.rmtree(self
.path
)
58 def skip_cleanup(self
):
59 # Set path to none so it's not deleted
63 class SConsFixture(Fixture
):
65 Fixture will wait until all SCons targets are collected and tests are
66 about to be ran, then will invocate a single instance of SCons for all
69 :param directory: The directory which scons will -C (cd) into before
70 executing. If None is provided, will choose the config base_dir.
72 def __init__(self
, directory
=None, target_class
=None):
73 self
.directory
= directory
if directory
else config
.base_dir
74 self
.target_class
= target_class
if target_class
else SConsTarget
75 self
.threads
= config
.threads
77 super(SConsFixture
, self
).__init
__()
79 def setup(self
, testitem
):
84 'scons', '-C', self
.directory
,
85 '-j', str(self
.threads
),
91 'No SCons targets specified, this will'
92 ' build the default all target.\n'
93 'This is likely unintended, and you'
94 ' may wish to kill testlib and reconfigure.')
97 'Building the following targets.'
98 ' This may take a while.')
99 log
.test_log
.message('%s' % (', '.join(self
.targets
)))
100 log
.test_log
.message(
101 "You may want to run with only a single ISA"
102 "(--isa=), use --skip-build, or use 'rerun'.")
106 command
.extend(self
.targets
)
107 log_call(log
.test_log
, command
)
110 class SConsTarget(Fixture
):
111 # The singleton scons fixture we'll use for all targets.
112 default_scons_invocation
= None
114 def __init__(self
, target
, build_dir
=None, invocation
=None):
116 Represents a target to be built by an 'invocation' of scons.
118 :param target: The target known to scons.
120 :param build_dir: The 'build' directory path which will be prepended
123 :param invocation: Represents an invocation of scons which we will
124 automatically attach this target to. If None provided, uses the
125 main 'scons' invocation.
128 if build_dir
is None:
129 build_dir
= config
.build_dir
130 self
.target
= os
.path
.join(build_dir
, target
)
131 super(SConsTarget
, self
).__init
__(name
=target
)
133 if invocation
is None:
134 if self
.default_scons_invocation
is None:
135 SConsTarget
.default_scons_invocation
= SConsFixture()
136 globalfixture(SConsTarget
.default_scons_invocation
)
138 invocation
= self
.default_scons_invocation
139 self
.invocation
= invocation
141 def schedule_finalized(self
, schedule
):
142 self
.invocation
.targets
.add(self
.target
)
143 return Fixture
.schedule_finalized(self
, schedule
)
145 class Gem5Fixture(SConsTarget
):
146 def __init__(self
, isa
, variant
):
147 target
= joinpath(isa
.upper(), 'gem5.%s' % variant
)
148 super(Gem5Fixture
, self
).__init
__(target
)
150 self
.name
= constants
.gem5_binary_fixture_name
151 self
.path
= self
.target
153 self
.variant
= variant
156 class MakeFixture(Fixture
):
157 def __init__(self
, directory
, *args
, **kwargs
):
158 name
= 'make -C %s' % directory
159 super(MakeFixture
, self
).__init
__(build_once
=True, lazy_init
=False,
163 self
.directory
= directory
166 super(MakeFixture
, self
).setup()
167 targets
= set(self
.required_by
)
168 command
= ['make', '-C', self
.directory
]
169 command
.extend([target
.target
for target
in targets
])
173 class MakeTarget(Fixture
):
174 def __init__(self
, target
, make_fixture
=None, *args
, **kwargs
):
176 :param make_fixture: The make invocation we will be attached to.
177 Since we don't have a single global instance of make in gem5 like we do
178 scons we need to know what invocation to attach to. If none given,
181 super(MakeTarget
, self
).__init
__(name
=target
, *args
, **kwargs
)
182 self
.target
= self
.name
184 if make_fixture
is None:
185 make_fixture
= MakeFixture(
190 self
.make_fixture
= make_fixture
192 # Add our self to the required targets of the main MakeFixture
193 self
.require(self
.make_fixture
)
195 def setup(self
, testitem
):
196 super(MakeTarget
, self
).setup()
197 self
.make_fixture
.setup()
200 class TestProgram(MakeTarget
):
201 def __init__(self
, program
, isa
, os
, recompile
=False):
202 make_dir
= joinpath('test-progs', program
)
203 make_fixture
= MakeFixture(make_dir
)
204 target
= joinpath('bin', isa
, os
, program
)
205 super(TestProgram
, self
).__init
__(target
, make_fixture
)
206 self
.path
= joinpath(make_dir
, target
)
207 self
.recompile
= recompile
209 def setup(self
, testitem
):
210 # Check if the program exists if it does then only compile if
211 # recompile was given.
213 super(MakeTarget
, self
).setup()
214 elif not os
.path
.exists(self
.path
):
215 super(MakeTarget
, self
).setup()
217 class DownloadedProgram(Fixture
):
218 """ Like TestProgram, but checks the version in the gem5 binary repository
219 and downloads an updated version if it is needed.
221 urlbase
= "http://gem5.org/dist/current/"
223 def __init__(self
, path
, program
, **kwargs
):
226 The path to the directory containing the binary relative to
229 The name of the binary file
231 super(DownloadedProgram
, self
).__init
__("download-" + program
,
232 build_once
=True, **kwargs
)
234 self
.program_dir
= path
235 relative_path
= joinpath(self
.program_dir
, program
)
236 self
.url
= self
.urlbase
+ relative_path
237 self
.path
= os
.path
.realpath(
238 joinpath(absdirpath(__file__
), '../', relative_path
)
244 log
.test_log
.debug("Downloading " + self
.url
+ " to " + self
.path
)
245 if not os
.path
.exists(self
.program_dir
):
247 os
.makedirs(self
.program_dir
)
249 if e
.errno
!= errno
.EEXIST
:
251 urllib
.urlretrieve(self
.url
, self
.path
)
253 def _getremotetime(self
):
254 import urllib2
, datetime
, time
255 import _strptime
# Needed for python threading bug
257 u
= urllib2
.urlopen(self
.url
)
258 return time
.mktime(datetime
.datetime
.strptime( \
259 u
.info().getheaders("Last-Modified")[0],
260 "%a, %d %b %Y %X GMT").timetuple())
262 def setup(self
, testitem
):
264 # Check to see if there is a file downloaded
265 if not os
.path
.exists(self
.path
):
269 t
= self
._getremotetime
()
270 except urllib2
.URLError
:
271 # Problem checking the server, use the old files.
272 log
.debug("Could not contact server. Binaries may be old.")
274 # If the server version is more recent, download it
275 if t
> os
.path
.getmtime(self
.path
):