summaryrefslogtreecommitdiff
path: root/hgdistver.py
blob: 13a7d5ec5e1c28a9a892bf571827b318a8dce62b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import os
import sys
import subprocess
def getoutput(cmd, cwd='.'):
    p = subprocess.Popen(cmd,
                         shell=True,
                         stdout=subprocess.PIPE,
                         cwd=cwd,
                        )
    out, _ = p.communicate()
    return out.decode() # will kill us sometimes

hg_prefix = 'hg '

def hg(args, cwd='.'):
    return getoutput(hg_prefix + args, cwd)

def version_from_cachefile(root, cachefile=None):
    #XXX: for now we ignore root
    if not cachefile:
        return
    #replaces 'with open()' from py2.6
    fd = open(cachefile)
    fd.readline()  # remove the comment
    version = None
    try:
        line = fd.readline()
        version_string = line.split(' = ')[1].strip()
        version = version_string[1:-1]
    except:  # any error means invalid cachefile
        pass
    fd.close()
    return version


def version_from_hg_id(root, cachefile=None):
    """stolen logic from mercurials setup.py as well"""
    if os.path.isdir(os.path.join(root, '.hg')):
        l = hg('id -i -t', root).strip().split()
        while len(l) > 1 and str(l[-1][0]).isalpha():  # remove non-numbered tags
            l.pop()
        if len(l) > 1:  # tag found
            version = str(l[-1])
            if str(l[0]).endswith('+'):  # propagate the dirty status to the tag
                version += '+'
            return version


def version_from_hg15_parents(root, cachefile=None):
    if os.path.isdir(os.path.join(root, '.hg')):
        hgver_out = hg('--version')
        hgver_out = hgver_out.splitlines()[0].rstrip(')')
        hgver = hgver_out.split('version ')[-1]
        if hgver < '1.5':
            return version_from_hg_log_with_tags(root)
        node = getoutput('hg id -i', root).strip()
        if node.strip('+') == '000000000000':
            return '0.0.dev0-' + node

        cmd = 'parents --template "{latesttag} {latesttagdistance}"'
        out = hg(cmd, root)
        try:
            tag, dist = out.split()
            if tag == 'null':
                tag = '0.0'
            return '%s.dev%s-%s' % (tag, dist, node)
        except ValueError:
            pass  # unpacking failed, old hg


def version_from_hg_log_with_tags(root, cachefile=None):
    if os.path.isdir(os.path.join(root, '.hg')):
        node = getoutput('hg id -i', root).strip()
        cmd = r'log -r %s:0 --template "{tags} \n"'
        cmd = cmd % node.rstrip('+')
        proc = subprocess.Popen(hg_prefix + cmd,
                                cwd=root,
                                shell=True,
                                stdout=subprocess.PIPE,
                                env={'ew':'why'},
                               )
        dist = -1  # no revs vs one rev is tricky

        for dist, line in enumerate(proc.stdout):
            line = line.decode()
            tags = [t for t in line.split() if not t.isalpha()]
            if tags:
                return '%s.dev%s-%s' % (tags[0], dist, node)

        return  '0.0.dev%s-%s' % (dist + 1, node)


def _archival_to_version(data):
    """stolen logic from mercurials setup.py"""
    if 'tag' in data:
        return data['tag']
    elif 'latesttag' in data:
        return '%(latesttag)s.dev%(latesttagdistance)s-%(node).12s' % data
    else:
        return data.get('node', '')[:12]


def _data_from_archival(path):
    import email
    data = email.message_from_file(open(str(path)))
    return dict(data.items())


def version_from_archival(root, cachefile=None):
    for parent in root, os.path.dirname(root):
        archival = os.path.join(parent, '.hg_archival.txt')
        if os.path.exists(archival):
            data = _data_from_archival(archival)
            return _archival_to_version(data)


def version_from_sdist_pkginfo(root, cachefile=None):
    pkginfo = os.path.join(root, 'PKG-INFO')
    if cachefile is None and os.path.exists(pkginfo):
        data = _data_from_archival(pkginfo)
        version = data.get('Version')
        if version != 'UNKNOWN':
            return version


def write_cachefile(path, version):
    fd = open(path, 'w')
    try:
        fd.write('# this file is autogenerated by hgdistver + setup.py\n')
        fd.write('version = %r' % version)
    finally:
        fd.close()


methods = [
    version_from_hg_id,
    version_from_hg15_parents,
    version_from_hg_log_with_tags,
    version_from_cachefile,
    version_from_sdist_pkginfo,
    version_from_archival,
]


def get_version(cachefile=None, root=None):
    if root is None:
        root = os.getcwd()
    if cachefile is not None:
        cachefile = os.path.join(root, cachefile)
    try:
        version = None
        for method in methods:
            version = method(root=root, cachefile=cachefile)
            if version:
                if version.endswith('+'):
                    import time
                    version += time.strftime('%Y%m%d')
                return version
    finally:
        if cachefile and version:
            write_cachefile(cachefile, version)