Update iCE40 GLOBAL and PULLUP attribute to use correct types.
[nmigen-boards.git] / nmigen_boards / _version.py
1
2 # This file helps to compute a version number in source trees obtained from
3 # git-archive tarball (such as those provided by githubs download-from-tag
4 # feature). Distribution tarballs (built by setup.py sdist) and build
5 # directories (produced by setup.py build) will contain a much shorter file
6 # that just contains the computed version number.
7
8 # This file is released into the public domain. Generated by
9 # versioneer-0.18 (https://github.com/warner/python-versioneer)
10
11 """Git implementation of _version.py."""
12
13 import errno
14 import os
15 import re
16 import subprocess
17 import sys
18
19
20 def get_keywords():
21 """Get the keywords needed to look up the version information."""
22 # these strings will be replaced by git during git-archive.
23 # setup.py/versioneer.py will grep for the variable names, so they must
24 # each be defined on a line of their own. _version.py will just call
25 # get_keywords().
26 git_refnames = "$Format:%d$"
27 git_full = "$Format:%H$"
28 git_date = "$Format:%ci$"
29 keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
30 return keywords
31
32
33 class VersioneerConfig:
34 """Container for Versioneer configuration parameters."""
35
36
37 def get_config():
38 """Create, populate and return the VersioneerConfig() object."""
39 # these strings are filled in when 'setup.py versioneer' creates
40 # _version.py
41 cfg = VersioneerConfig()
42 cfg.VCS = "git"
43 cfg.style = "git-describe-long"
44 cfg.tag_prefix = "v"
45 cfg.parentdir_prefix = "nmigen_boards-"
46 cfg.versionfile_source = "nmigen_boards/_version.py"
47 cfg.verbose = False
48 return cfg
49
50
51 class NotThisMethod(Exception):
52 """Exception raised if a method is not valid for the current scenario."""
53
54
55 LONG_VERSION_PY = {}
56 HANDLERS = {}
57
58
59 def register_vcs_handler(vcs, method): # decorator
60 """Decorator to mark a method as the handler for a particular VCS."""
61 def decorate(f):
62 """Store f in HANDLERS[vcs][method]."""
63 if vcs not in HANDLERS:
64 HANDLERS[vcs] = {}
65 HANDLERS[vcs][method] = f
66 return f
67 return decorate
68
69
70 def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
71 env=None):
72 """Call the given command(s)."""
73 assert isinstance(commands, list)
74 p = None
75 for c in commands:
76 try:
77 dispcmd = str([c] + args)
78 # remember shell=False, so use git.cmd on windows, not just git
79 p = subprocess.Popen([c] + args, cwd=cwd, env=env,
80 stdout=subprocess.PIPE,
81 stderr=(subprocess.PIPE if hide_stderr
82 else None))
83 break
84 except EnvironmentError:
85 e = sys.exc_info()[1]
86 if e.errno == errno.ENOENT:
87 continue
88 if verbose:
89 print("unable to run %s" % dispcmd)
90 print(e)
91 return None, None
92 else:
93 if verbose:
94 print("unable to find command, tried %s" % (commands,))
95 return None, None
96 stdout = p.communicate()[0].strip()
97 if sys.version_info[0] >= 3:
98 stdout = stdout.decode()
99 if p.returncode != 0:
100 if verbose:
101 print("unable to run %s (error)" % dispcmd)
102 print("stdout was %s" % stdout)
103 return None, p.returncode
104 return stdout, p.returncode
105
106
107 def versions_from_parentdir(parentdir_prefix, root, verbose):
108 """Try to determine the version from the parent directory name.
109
110 Source tarballs conventionally unpack into a directory that includes both
111 the project name and a version string. We will also support searching up
112 two directory levels for an appropriately named parent directory
113 """
114 rootdirs = []
115
116 for i in range(3):
117 dirname = os.path.basename(root)
118 if dirname.startswith(parentdir_prefix):
119 return {"version": dirname[len(parentdir_prefix):],
120 "full-revisionid": None,
121 "dirty": False, "error": None, "date": None}
122 else:
123 rootdirs.append(root)
124 root = os.path.dirname(root) # up a level
125
126 if verbose:
127 print("Tried directories %s but none started with prefix %s" %
128 (str(rootdirs), parentdir_prefix))
129 raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
130
131
132 @register_vcs_handler("git", "get_keywords")
133 def git_get_keywords(versionfile_abs):
134 """Extract version information from the given file."""
135 # the code embedded in _version.py can just fetch the value of these
136 # keywords. When used from setup.py, we don't want to import _version.py,
137 # so we do it with a regexp instead. This function is not used from
138 # _version.py.
139 keywords = {}
140 try:
141 f = open(versionfile_abs, "r")
142 for line in f.readlines():
143 if line.strip().startswith("git_refnames ="):
144 mo = re.search(r'=\s*"(.*)"', line)
145 if mo:
146 keywords["refnames"] = mo.group(1)
147 if line.strip().startswith("git_full ="):
148 mo = re.search(r'=\s*"(.*)"', line)
149 if mo:
150 keywords["full"] = mo.group(1)
151 if line.strip().startswith("git_date ="):
152 mo = re.search(r'=\s*"(.*)"', line)
153 if mo:
154 keywords["date"] = mo.group(1)
155 f.close()
156 except EnvironmentError:
157 pass
158 return keywords
159
160
161 @register_vcs_handler("git", "keywords")
162 def git_versions_from_keywords(keywords, tag_prefix, verbose):
163 """Get version information from git keywords."""
164 if not keywords:
165 raise NotThisMethod("no keywords at all, weird")
166 date = keywords.get("date")
167 if date is not None:
168 # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant
169 # datestamp. However we prefer "%ci" (which expands to an "ISO-8601
170 # -like" string, which we must then edit to make compliant), because
171 # it's been around since git-1.5.3, and it's too difficult to
172 # discover which version we're using, or to work around using an
173 # older one.
174 date = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
175 refnames = keywords["refnames"].strip()
176 if refnames.startswith("$Format"):
177 if verbose:
178 print("keywords are unexpanded, not using")
179 raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
180 refs = set([r.strip() for r in refnames.strip("()").split(",")])
181 # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
182 # just "foo-1.0". If we see a "tag: " prefix, prefer those.
183 TAG = "tag: "
184 tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
185 if not tags:
186 # Either we're using git < 1.8.3, or there really are no tags. We use
187 # a heuristic: assume all version tags have a digit. The old git %d
188 # expansion behaves like git log --decorate=short and strips out the
189 # refs/heads/ and refs/tags/ prefixes that would let us distinguish
190 # between branches and tags. By ignoring refnames without digits, we
191 # filter out many common branch names like "release" and
192 # "stabilization", as well as "HEAD" and "master".
193 tags = set([r for r in refs if re.search(r'\d', r)])
194 if verbose:
195 print("discarding '%s', no digits" % ",".join(refs - tags))
196 if verbose:
197 print("likely tags: %s" % ",".join(sorted(tags)))
198 for ref in sorted(tags):
199 # sorting will prefer e.g. "2.0" over "2.0rc1"
200 if ref.startswith(tag_prefix):
201 r = ref[len(tag_prefix):]
202 if verbose:
203 print("picking %s" % r)
204 return {"version": r,
205 "full-revisionid": keywords["full"].strip(),
206 "dirty": False, "error": None,
207 "date": date}
208 # no suitable tags, so version is "0+unknown", but full hex is still there
209 if verbose:
210 print("no suitable tags, using unknown + full revision id")
211 return {"version": "0+unknown",
212 "full-revisionid": keywords["full"].strip(),
213 "dirty": False, "error": "no suitable tags", "date": None}
214
215
216 @register_vcs_handler("git", "pieces_from_vcs")
217 def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
218 """Get version from 'git describe' in the root of the source tree.
219
220 This only gets called if the git-archive 'subst' keywords were *not*
221 expanded, and _version.py hasn't already been rewritten with a short
222 version string, meaning we're inside a checked out source tree.
223 """
224 GITS = ["git"]
225 if sys.platform == "win32":
226 GITS = ["git.cmd", "git.exe"]
227
228 out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root,
229 hide_stderr=True)
230 if rc != 0:
231 if verbose:
232 print("Directory %s not under git control" % root)
233 raise NotThisMethod("'git rev-parse --git-dir' returned error")
234
235 # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
236 # if there isn't one, this yields HEX[-dirty] (no NUM)
237 describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty",
238 "--always", "--long",
239 "--match", "%s*" % tag_prefix],
240 cwd=root)
241 # --long was added in git-1.5.5
242 if describe_out is None:
243 raise NotThisMethod("'git describe' failed")
244 describe_out = describe_out.strip()
245 full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
246 if full_out is None:
247 raise NotThisMethod("'git rev-parse' failed")
248 full_out = full_out.strip()
249
250 pieces = {}
251 pieces["long"] = full_out
252 pieces["short"] = full_out[:7] # maybe improved later
253 pieces["error"] = None
254
255 # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
256 # TAG might have hyphens.
257 git_describe = describe_out
258
259 # look for -dirty suffix
260 dirty = git_describe.endswith("-dirty")
261 pieces["dirty"] = dirty
262 if dirty:
263 git_describe = git_describe[:git_describe.rindex("-dirty")]
264
265 # now we have TAG-NUM-gHEX or HEX
266
267 if "-" in git_describe:
268 # TAG-NUM-gHEX
269 mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
270 if not mo:
271 # unparseable. Maybe git-describe is misbehaving?
272 pieces["error"] = ("unable to parse git-describe output: '%s'"
273 % describe_out)
274 return pieces
275
276 # tag
277 full_tag = mo.group(1)
278 if not full_tag.startswith(tag_prefix):
279 if verbose:
280 fmt = "tag '%s' doesn't start with prefix '%s'"
281 print(fmt % (full_tag, tag_prefix))
282 pieces["error"] = ("tag '%s' doesn't start with prefix '%s'"
283 % (full_tag, tag_prefix))
284 return pieces
285 pieces["closest-tag"] = full_tag[len(tag_prefix):]
286
287 # distance: number of commits since tag
288 pieces["distance"] = int(mo.group(2))
289
290 # commit: short hex revision ID
291 pieces["short"] = mo.group(3)
292
293 else:
294 # HEX: no tags
295 pieces["closest-tag"] = None
296 count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"],
297 cwd=root)
298 pieces["distance"] = int(count_out) # total number of commits
299
300 # commit date: see ISO-8601 comment in git_versions_from_keywords()
301 date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"],
302 cwd=root)[0].strip()
303 pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
304
305 return pieces
306
307
308 def plus_or_dot(pieces):
309 """Return a + if we don't already have one, else return a ."""
310 if "+" in pieces.get("closest-tag", ""):
311 return "."
312 return "+"
313
314
315 def render_pep440(pieces):
316 """Build up version string, with post-release "local version identifier".
317
318 Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
319 get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
320
321 Exceptions:
322 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
323 """
324 if pieces["closest-tag"]:
325 rendered = pieces["closest-tag"]
326 if pieces["distance"] or pieces["dirty"]:
327 rendered += plus_or_dot(pieces)
328 rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
329 if pieces["dirty"]:
330 rendered += ".dirty"
331 else:
332 # exception #1
333 rendered = "0+untagged.%d.g%s" % (pieces["distance"],
334 pieces["short"])
335 if pieces["dirty"]:
336 rendered += ".dirty"
337 return rendered
338
339
340 def render_pep440_pre(pieces):
341 """TAG[.post.devDISTANCE] -- No -dirty.
342
343 Exceptions:
344 1: no tags. 0.post.devDISTANCE
345 """
346 if pieces["closest-tag"]:
347 rendered = pieces["closest-tag"]
348 if pieces["distance"]:
349 rendered += ".post.dev%d" % pieces["distance"]
350 else:
351 # exception #1
352 rendered = "0.post.dev%d" % pieces["distance"]
353 return rendered
354
355
356 def render_pep440_post(pieces):
357 """TAG[.postDISTANCE[.dev0]+gHEX] .
358
359 The ".dev0" means dirty. Note that .dev0 sorts backwards
360 (a dirty tree will appear "older" than the corresponding clean one),
361 but you shouldn't be releasing software with -dirty anyways.
362
363 Exceptions:
364 1: no tags. 0.postDISTANCE[.dev0]
365 """
366 if pieces["closest-tag"]:
367 rendered = pieces["closest-tag"]
368 if pieces["distance"] or pieces["dirty"]:
369 rendered += ".post%d" % pieces["distance"]
370 if pieces["dirty"]:
371 rendered += ".dev0"
372 rendered += plus_or_dot(pieces)
373 rendered += "g%s" % pieces["short"]
374 else:
375 # exception #1
376 rendered = "0.post%d" % pieces["distance"]
377 if pieces["dirty"]:
378 rendered += ".dev0"
379 rendered += "+g%s" % pieces["short"]
380 return rendered
381
382
383 def render_pep440_old(pieces):
384 """TAG[.postDISTANCE[.dev0]] .
385
386 The ".dev0" means dirty.
387
388 Eexceptions:
389 1: no tags. 0.postDISTANCE[.dev0]
390 """
391 if pieces["closest-tag"]:
392 rendered = pieces["closest-tag"]
393 if pieces["distance"] or pieces["dirty"]:
394 rendered += ".post%d" % pieces["distance"]
395 if pieces["dirty"]:
396 rendered += ".dev0"
397 else:
398 # exception #1
399 rendered = "0.post%d" % pieces["distance"]
400 if pieces["dirty"]:
401 rendered += ".dev0"
402 return rendered
403
404
405 def render_git_describe(pieces):
406 """TAG[-DISTANCE-gHEX][-dirty].
407
408 Like 'git describe --tags --dirty --always'.
409
410 Exceptions:
411 1: no tags. HEX[-dirty] (note: no 'g' prefix)
412 """
413 if pieces["closest-tag"]:
414 rendered = pieces["closest-tag"]
415 if pieces["distance"]:
416 rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
417 else:
418 # exception #1
419 rendered = pieces["short"]
420 if pieces["dirty"]:
421 rendered += "-dirty"
422 return rendered
423
424
425 def render_git_describe_long(pieces):
426 """TAG-DISTANCE-gHEX[-dirty].
427
428 Like 'git describe --tags --dirty --always -long'.
429 The distance/hash is unconditional.
430
431 Exceptions:
432 1: no tags. HEX[-dirty] (note: no 'g' prefix)
433 """
434 if pieces["closest-tag"]:
435 rendered = pieces["closest-tag"]
436 rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
437 else:
438 # exception #1
439 rendered = pieces["short"]
440 if pieces["dirty"]:
441 rendered += "-dirty"
442 return rendered
443
444
445 def render(pieces, style):
446 """Render the given version pieces into the requested style."""
447 if pieces["error"]:
448 return {"version": "unknown",
449 "full-revisionid": pieces.get("long"),
450 "dirty": None,
451 "error": pieces["error"],
452 "date": None}
453
454 if not style or style == "default":
455 style = "pep440" # the default
456
457 if style == "pep440":
458 rendered = render_pep440(pieces)
459 elif style == "pep440-pre":
460 rendered = render_pep440_pre(pieces)
461 elif style == "pep440-post":
462 rendered = render_pep440_post(pieces)
463 elif style == "pep440-old":
464 rendered = render_pep440_old(pieces)
465 elif style == "git-describe":
466 rendered = render_git_describe(pieces)
467 elif style == "git-describe-long":
468 rendered = render_git_describe_long(pieces)
469 else:
470 raise ValueError("unknown style '%s'" % style)
471
472 return {"version": rendered, "full-revisionid": pieces["long"],
473 "dirty": pieces["dirty"], "error": None,
474 "date": pieces.get("date")}
475
476
477 def get_versions():
478 """Get version information or return default if unable to do so."""
479 # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
480 # __file__, we can work backwards from there to the root. Some
481 # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
482 # case we can only use expanded keywords.
483
484 cfg = get_config()
485 verbose = cfg.verbose
486
487 try:
488 return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
489 verbose)
490 except NotThisMethod:
491 pass
492
493 try:
494 root = os.path.realpath(__file__)
495 # versionfile_source is the relative path from the top of the source
496 # tree (where the .git directory might live) to this file. Invert
497 # this to find the root from __file__.
498 for i in cfg.versionfile_source.split('/'):
499 root = os.path.dirname(root)
500 except NameError:
501 return {"version": "0+unknown", "full-revisionid": None,
502 "dirty": None,
503 "error": "unable to find root of source tree",
504 "date": None}
505
506 try:
507 pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
508 return render(pieces, cfg.style)
509 except NotThisMethod:
510 pass
511
512 try:
513 if cfg.parentdir_prefix:
514 return versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
515 except NotThisMethod:
516 pass
517
518 return {"version": "0+unknown", "full-revisionid": None,
519 "dirty": None,
520 "error": "unable to compute version", "date": None}