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
)
59 class SConsFixture(Fixture
):
61 Fixture will wait until all SCons targets are collected and tests are
62 about to be ran, then will invocate a single instance of SCons for all
65 :param directory: The directory which scons will -C (cd) into before
66 executing. If None is provided, will choose the config base_dir.
68 def __init__(self
, directory
=None, target_class
=None):
69 self
.directory
= directory
if directory
else config
.base_dir
70 self
.target_class
= target_class
if target_class
else SConsTarget
71 self
.threads
= config
.threads
73 super(SConsFixture
, self
).__init
__()
75 def setup(self
, testitem
):
80 'scons', '-C', self
.directory
,
81 '-j', str(self
.threads
),
87 'No SCons targets specified, this will'
88 ' build the default all target.\n'
89 'This is likely unintended, and you'
90 ' may wish to kill testlib and reconfigure.')
93 'Building the following targets.'
94 ' This may take a while.')
95 log
.test_log
.message('%s' % (', '.join(self
.targets
)))
97 "You may want to run with only a single ISA"
98 "(--isa=), use --skip-build, or use 'rerun'.")
102 command
.extend(self
.targets
)
103 log_call(log
.test_log
, command
)
106 class SConsTarget(Fixture
):
107 # The singleton scons fixture we'll use for all targets.
108 default_scons_invocation
= None
110 def __init__(self
, target
, build_dir
=None, invocation
=None):
112 Represents a target to be built by an 'invocation' of scons.
114 :param target: The target known to scons.
116 :param build_dir: The 'build' directory path which will be prepended
119 :param invocation: Represents an invocation of scons which we will
120 automatically attach this target to. If None provided, uses the
121 main 'scons' invocation.
124 if build_dir
is None:
125 build_dir
= config
.build_dir
126 self
.target
= os
.path
.join(build_dir
, target
)
127 super(SConsTarget
, self
).__init
__(name
=target
)
129 if invocation
is None:
130 if self
.default_scons_invocation
is None:
131 SConsTarget
.default_scons_invocation
= SConsFixture()
132 globalfixture(SConsTarget
.default_scons_invocation
)
134 invocation
= self
.default_scons_invocation
135 self
.invocation
= invocation
137 def schedule_finalized(self
, schedule
):
138 self
.invocation
.targets
.add(self
.target
)
139 return Fixture
.schedule_finalized(self
, schedule
)
141 class Gem5Fixture(SConsTarget
):
142 def __init__(self
, isa
, variant
):
143 target
= joinpath(isa
.upper(), 'gem5.%s' % variant
)
144 super(Gem5Fixture
, self
).__init
__(target
)
146 self
.name
= constants
.gem5_binary_fixture_name
147 self
.path
= self
.target
149 self
.variant
= variant
152 class MakeFixture(Fixture
):
153 def __init__(self
, directory
, *args
, **kwargs
):
154 name
= 'make -C %s' % directory
155 super(MakeFixture
, self
).__init
__(build_once
=True, lazy_init
=False,
159 self
.directory
= directory
162 super(MakeFixture
, self
).setup()
163 targets
= set(self
.required_by
)
164 command
= ['make', '-C', self
.directory
]
165 command
.extend([target
.target
for target
in targets
])
169 class MakeTarget(Fixture
):
170 def __init__(self
, target
, make_fixture
=None, *args
, **kwargs
):
172 :param make_fixture: The make invocation we will be attached to.
173 Since we don't have a single global instance of make in gem5 like we do
174 scons we need to know what invocation to attach to. If none given,
177 super(MakeTarget
, self
).__init
__(name
=target
, *args
, **kwargs
)
178 self
.target
= self
.name
180 if make_fixture
is None:
181 make_fixture
= MakeFixture(
186 self
.make_fixture
= make_fixture
188 # Add our self to the required targets of the main MakeFixture
189 self
.require(self
.make_fixture
)
191 def setup(self
, testitem
):
192 super(MakeTarget
, self
).setup()
193 self
.make_fixture
.setup()
196 class TestProgram(MakeTarget
):
197 def __init__(self
, program
, isa
, os
, recompile
=False):
198 make_dir
= joinpath('test-progs', program
)
199 make_fixture
= MakeFixture(make_dir
)
200 target
= joinpath('bin', isa
, os
, program
)
201 super(TestProgram
, self
).__init
__(target
, make_fixture
)
202 self
.path
= joinpath(make_dir
, target
)
203 self
.recompile
= recompile
205 def setup(self
, testitem
):
206 # Check if the program exists if it does then only compile if
207 # recompile was given.
209 super(MakeTarget
, self
).setup()
210 elif not os
.path
.exists(self
.path
):
211 super(MakeTarget
, self
).setup()
213 class DownloadedProgram(Fixture
):
214 """ Like TestProgram, but checks the version in the gem5 binary repository
215 and downloads an updated version if it is needed.
217 urlbase
= "http://gem5.org/dist/current/"
219 def __init__(self
, path
, program
, **kwargs
):
220 super(DownloadedProgram
, self
).__init
__("download-" + program
,
221 build_once
=True, **kwargs
)
223 self
.program_dir
= joinpath('test-progs', path
)
224 self
.path
= joinpath(self
.program_dir
, program
)
226 self
.url
= self
.urlbase
+ self
.path
230 log
.test_log
.debug("Downloading " + self
.url
+ " to " + self
.path
)
231 if not os
.path
.exists(self
.program_dir
):
232 os
.makedirs(self
.program_dir
)
233 urllib
.urlretrieve(self
.url
, self
.path
)
235 def _getremotetime(self
):
236 import urllib2
, datetime
, time
237 import _strptime
# Needed for python threading bug
239 u
= urllib2
.urlopen(self
.url
)
240 return time
.mktime(datetime
.datetime
.strptime( \
241 u
.info().getheaders("Last-Modified")[0],
242 "%a, %d %b %Y %X GMT").timetuple())
244 def setup(self
, testitem
):
246 # Check to see if there is a file downloaded
247 if not os
.path
.exists(self
.path
):
251 t
= self
._getremotetime
()
252 except urllib2
.URLError
:
253 # Problem checking the server, use the old files.
254 log
.debug("Could not contact server. Binaries may be old.")
256 # If the server version is more recent, download it
257 if t
> os
.path
.getmtime(self
.path
):