1 #!/usr/bin/env python2.7
3 # Copyright (c) 2012-2013,2015-2016 ARM Limited
6 # The license below extends only to copyright in the software and shall
7 # not be construed as granting a license to any other intellectual
8 # property including but not limited to intellectual property relating
9 # to a hardware implementation of the functionality of the software
10 # licensed hereunder. You may use the software subject to the license
11 # terms below provided that you ensure that this notice is replicated
12 # unmodified and in its entirety in all distributions of the software,
13 # modified or unmodified, in source code or in binary form.
15 # Redistribution and use in source and binary forms, with or without
16 # modification, are permitted provided that the following conditions are
17 # met: redistributions of source code must retain the above copyright
18 # notice, this list of conditions and the following disclaimer;
19 # redistributions in binary form must reproduce the above copyright
20 # notice, this list of conditions and the following disclaimer in the
21 # documentation and/or other materials provided with the distribution;
22 # neither the name of the copyright holders nor the names of its
23 # contributors may be used to endorse or promote products derived from
24 # this software without specific prior written permission.
26 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 # This python code is used to migrate checkpoints that were created in one
43 # version of the simulator to newer version. As features are added or bugs are
44 # fixed some of the state that needs to be checkpointed can change. If you have
45 # many historic checkpoints that you use, manually editing them to fix them is
46 # both time consuming and error-prone.
48 # This script provides a way to migrate checkpoints to the newer repository in
49 # a programmatic way. It can be imported into another script or used on the
50 # command line. From the command line the script will either migrate every
51 # checkpoint it finds recursively (-r option) or a single checkpoint. When a
52 # change is made to the gem5 repository that breaks previous checkpoints an
53 # upgrade() method should be implemented in its own .py file and placed in
54 # src/util/cpt_upgraders/. For each upgrader whose tag is not present in
55 # the checkpoint tag list, the upgrade() method will be run, passing in a
56 # ConfigParser object which contains the open file. As these operations can
57 # be isa specific the method can verify the isa and use regexes to find the
58 # correct sections that need to be updated.
60 # It is also possible to use this mechanism to revert prior tags. In this
61 # case, implement a downgrade() method instead. Dependencies should still
62 # work naturally - a tag depending on a tag with a downgrader means that it
63 # insists on the other tag being removed and its downgrader executed before
64 # its upgrader (or downgrader) can run. It is still the case that a tag
65 # can only be used once.
67 # Dependencies between tags are expressed by two variables at the top-level
68 # of the upgrader script: "depends" can be either a string naming another
69 # tag that it depends upon or a list of such strings; and "fwd_depends"
70 # accepts the same datatypes but it reverses the sense of the dependency
71 # arrow(s) -- it expresses that that tag depends upon the tag of the current
72 # upgrader. This can be especially valuable when maintaining private
73 # upgraders in private branches.
77 import glob
, types
, sys
, os
82 def verboseprint(*args
):
91 untag_set
= set() # tags to remove by downgrading
94 def __init__(self
, filename
):
95 self
.filename
= filename
96 execfile(filename
, {}, self
.__dict
__)
98 if not hasattr(self
, 'tag'):
99 self
.tag
= osp
.basename(filename
)[:-3]
100 if not hasattr(self
, 'depends'):
102 elif isinstance(self
.depends
, str):
103 self
.depends
= [self
.depends
]
105 if not isinstance(self
.depends
, list):
106 print "Error: 'depends' for %s is the wrong type" % self
.tag
109 if hasattr(self
, 'fwd_depends'):
110 if isinstance(self
.fwd_depends
, str):
111 self
.fwd_depends
= [self
.fwd_depends
]
113 self
.fwd_depends
= []
115 if not isinstance(self
.fwd_depends
, list):
116 print "Error: 'fwd_depends' for %s is the wrong type" % self
.tag
119 if hasattr(self
, 'upgrader'):
120 if not isinstance(self
.upgrader
, types
.FunctionType
):
121 print "Error: 'upgrader' for %s is %s, not function" \
122 % (self
.tag
, type(self
))
124 Upgrader
.tag_set
.add(self
.tag
)
125 elif hasattr(self
, 'downgrader'):
126 if not isinstance(self
.downgrader
, types
.FunctionType
):
127 print "Error: 'downgrader' for %s is %s, not function" \
128 % (self
.tag
, type(self
))
130 Upgrader
.untag_set
.add(self
.tag
)
132 print "Error: no upgrader or downgrader method for", self
.tag
135 if hasattr(self
, 'legacy_version'):
136 Upgrader
.legacy
[self
.legacy_version
] = self
138 Upgrader
.by_tag
[self
.tag
] = self
140 def ready(self
, tags
):
141 for dep
in self
.depends
:
146 def update(self
, cpt
, tags
):
147 if hasattr(self
, 'upgrader'):
150 verboseprint("applied upgrade for", self
.tag
)
153 tags
.remove(self
.tag
)
154 verboseprint("applied downgrade for", self
.tag
)
158 return Upgrader
.by_tag
[tag
]
162 util_dir
= osp
.dirname(osp
.abspath(__file__
))
164 for py
in glob
.glob(util_dir
+ '/cpt_upgraders/*.py'):
167 # make linear dependences for legacy versions
169 while i
in Upgrader
.legacy
:
170 Upgrader
.legacy
[i
].depends
= [Upgrader
.legacy
[i
-1].tag
]
173 # resolve forward dependencies and audit normal dependencies
174 for tag
, upg
in Upgrader
.by_tag
.items():
175 for fd
in upg
.fwd_depends
:
176 if fd
not in Upgrader
.by_tag
:
177 print "Error: '%s' cannot (forward) depend on "\
178 "nonexistent tag '%s'" % (fd
, tag
)
180 Upgrader
.by_tag
[fd
].depends
.append(tag
)
181 for dep
in upg
.depends
:
182 if dep
not in Upgrader
.by_tag
:
183 print "Error: '%s' cannot depend on "\
184 "nonexistent tag '%s'" % (tag
, dep
)
187 def process_file(path
, **kwargs
):
188 if not osp
.isfile(path
):
190 raise IOError(ennro
.ENOENT
, "No such file", path
)
192 verboseprint("Processing file %s...." % path
)
194 if kwargs
.get('backup', True):
196 shutil
.copyfile(path
, path
+ '.bak')
198 cpt
= ConfigParser
.SafeConfigParser()
200 # gem5 is case sensitive with paramaters
201 cpt
.optionxform
= str
203 # Read the current data
204 cpt_file
= file(path
, 'r')
210 # Make sure we know what we're starting from
211 if cpt
.has_option('root','cpt_ver'):
212 cpt_ver
= cpt
.getint('root','cpt_ver')
214 # Legacy linear checkpoint version
215 # convert to list of tags before proceeding
217 for i
in xrange(2, cpt_ver
+1):
218 tags
.add(Upgrader
.legacy
[i
].tag
)
219 verboseprint("performed legacy version -> tags conversion")
222 cpt
.remove_option('root', 'cpt_ver')
223 elif cpt
.has_option('Globals','version_tags'):
224 tags
= set((''.join(cpt
.get('Globals','version_tags'))).split())
226 print "fatal: no version information in checkpoint"
229 verboseprint("has tags", ' '.join(tags
))
230 # If the current checkpoint has a tag we don't know about, we have
231 # a divergence that (in general) must be addressed by (e.g.) merging
232 # simulator support for its changes.
233 unknown_tags
= tags
- (Upgrader
.tag_set | Upgrader
.untag_set
)
235 print "warning: upgrade script does not recognize the following "\
236 "tags in this checkpoint:", ' '.join(unknown_tags
)
238 # Apply migrations for tags not in checkpoint and tags present for which
239 # downgraders are present, respecting dependences
240 to_apply
= (Upgrader
.tag_set
- tags
) |
(Upgrader
.untag_set
& tags
)
242 ready
= set([ t
for t
in to_apply
if Upgrader
.get(t
).ready(tags
) ])
244 print "could not apply these upgrades:", ' '.join(to_apply
)
245 print "update dependences impossible to resolve; aborting"
249 Upgrader
.get(tag
).update(cpt
, tags
)
255 verboseprint("...nothing to do")
258 cpt
.set('Globals', 'version_tags', ' '.join(tags
))
260 # Write the old data back
261 verboseprint("...completed")
262 cpt
.write(file(path
, 'w'))
264 if __name__
== '__main__':
265 from optparse
import OptionParser
, SUPPRESS_HELP
266 parser
= OptionParser("usage: %prog [options] <filename or directory>")
267 parser
.add_option("-r", "--recurse", action
="store_true",
268 help="Recurse through all subdirectories modifying "\
269 "each checkpoint that is found")
270 parser
.add_option("-N", "--no-backup", action
="store_false",
271 dest
="backup", default
=True,
272 help="Do no backup each checkpoint before modifying it")
273 parser
.add_option("-v", "--verbose", action
="store_true",
274 help="Print out debugging information as")
275 parser
.add_option("--get-cc-file", action
="store_true",
276 # used during build; generate src/sim/tags.cc and exit
279 (options
, args
) = parser
.parse_args()
280 verbose_print
= options
.verbose
284 if options
.get_cc_file
:
285 print "// this file is auto-generated by util/cpt_upgrader.py"
286 print "#include <string>"
287 print "#include <set>"
289 print "std::set<std::string> version_tags = {"
290 for tag
in Upgrader
.tag_set
:
291 print " \"%s\"," % tag
295 parser
.error("You must specify a checkpoint file to modify or a "\
296 "directory of checkpoints to recursively update")
298 # Deal with shell variables and ~
299 path
= osp
.expandvars(osp
.expanduser(args
[0]))
301 # Process a single file if we have it
303 process_file(path
, **vars(options
))
304 # Process an entire directory
305 elif osp
.isdir(path
):
306 cpt_file
= osp
.join(path
, 'm5.cpt')
308 # Visit very file and see if it matches
309 for root
,dirs
,files
in os
.walk(path
):
312 process_file(osp
.join(root
,name
), **vars(options
))
315 # Maybe someone passed a cpt.XXXXXXX directory and not m5.cpt
316 elif osp
.isfile(cpt_file
):
317 process_file(cpt_file
, **vars(options
))
319 print "Error: checkpoint file not found at in %s " % path
,
320 print "and recurse not specified"