wafsamba: samba_version: add samba version suffix to vcs_fields
[mat/samba.git] / buildtools / wafsamba / samba_version.py
1 import os
2 import Utils
3 import samba_utils
4 import sys
5
6 def bzr_version_summary(path):
7     try:
8         import bzrlib
9     except ImportError:
10         return ("BZR-UNKNOWN", {})
11
12     import bzrlib.ui
13     bzrlib.ui.ui_factory = bzrlib.ui.make_ui_for_terminal(
14         sys.stdin, sys.stdout, sys.stderr)
15     from bzrlib import branch, osutils, workingtree
16     from bzrlib.plugin import load_plugins
17     load_plugins()
18
19     b = branch.Branch.open(path)
20     (revno, revid) = b.last_revision_info()
21     rev = b.repository.get_revision(revid)
22
23     fields = {
24         "BZR_REVISION_ID": revid,
25         "BZR_REVNO": revno,
26         "COMMIT_DATE": osutils.format_date_with_offset_in_original_timezone(rev.timestamp,
27             rev.timezone or 0),
28         "COMMIT_TIME": int(rev.timestamp),
29         "BZR_BRANCH": rev.properties.get("branch-nick", ""),
30         }
31
32     # If possible, retrieve the git sha
33     try:
34         from bzrlib.plugins.git.object_store import get_object_store
35     except ImportError:
36         # No git plugin
37         ret = "BZR-%d" % revno
38     else:
39         store = get_object_store(b.repository)
40         store.lock_read()
41         try:
42             full_rev = store._lookup_revision_sha1(revid)
43         finally:
44             store.unlock()
45         fields["GIT_COMMIT_ABBREV"] = full_rev[:7]
46         fields["GIT_COMMIT_FULLREV"] = full_rev
47         ret = "GIT-" + fields["GIT_COMMIT_ABBREV"]
48
49     if workingtree.WorkingTree.open(path).has_changes():
50         fields["COMMIT_IS_CLEAN"] = 0
51         ret += "+"
52     else:
53         fields["COMMIT_IS_CLEAN"] = 1
54     return (ret, fields)
55
56
57 def git_version_summary(path, env=None):
58     # Get version from GIT
59     if not 'GIT' in env and os.path.exists("/usr/bin/git"):
60         # this is useful when doing make dist without configuring
61         env.GIT = "/usr/bin/git"
62
63     if not 'GIT' in env:
64         return ("GIT-UNKNOWN", {})
65
66     environ = dict(os.environ)
67     environ["GIT_DIR"] = '%s/.git' % path
68     environ["GIT_WORK_TREE"] = path
69     git = Utils.cmd_output(env.GIT + ' show --pretty=format:"%h%n%ct%n%H%n%cd" --stat HEAD', silent=True, env=environ)
70
71     lines = git.splitlines()
72     if not lines or len(lines) < 4:
73         return ("GIT-UNKNOWN", {})
74
75     fields = {
76             "GIT_COMMIT_ABBREV": lines[0],
77             "GIT_COMMIT_FULLREV": lines[2],
78             "COMMIT_TIME": int(lines[1]),
79             "COMMIT_DATE": lines[3],
80             }
81
82     ret = "GIT-" + fields["GIT_COMMIT_ABBREV"]
83
84     if env.GIT_LOCAL_CHANGES:
85         clean = Utils.cmd_output('%s diff HEAD | wc -l' % env.GIT, silent=True).strip()
86         if clean == "0":
87             fields["COMMIT_IS_CLEAN"] = 1
88         else:
89             fields["COMMIT_IS_CLEAN"] = 0
90             ret += "+"
91
92     return (ret, fields)
93
94
95 def distversion_version_summary(path):
96     #get version from .distversion file
97     f = open(path + '/.distversion', 'r')
98     suffix = None
99     fields = {}
100
101     for line in f:
102         line = line.strip()
103         if line == '':
104             continue
105         if line.startswith("#"):
106             continue
107         try:
108             split_line = line.split("=")
109             if split_line[1] != "":
110                 key = split_line[0]
111                 value = split_line[1]
112                 if key == "SUFFIX":
113                     suffix = value
114                     continue
115                 fields[key] = value
116         except:
117             print("Failed to parse line %s from .distversion file." % (line))
118             raise
119     f.close()
120
121     if "COMMIT_TIME" in fields:
122         fields["COMMIT_TIME"] = int(fields["COMMIT_TIME"])
123
124     if suffix is None:
125         return ("UNKNOWN", fields)
126
127     return (suffix, fields)
128
129
130 class SambaVersion(object):
131
132     def __init__(self, version_dict, path, env=None, is_install=True):
133         '''Determine the version number of samba
134
135 See VERSION for the format.  Entries on that file are 
136 also accepted as dictionary entries here
137         '''
138
139         self.MAJOR=None
140         self.MINOR=None
141         self.RELEASE=None
142         self.REVISION=None
143         self.TP_RELEASE=None
144         self.ALPHA_RELEASE=None
145         self.BETA_RELEASE=None
146         self.PRE_RELEASE=None
147         self.RC_RELEASE=None
148         self.IS_SNAPSHOT=True
149         self.RELEASE_NICKNAME=None
150         self.VENDOR_SUFFIX=None
151         self.VENDOR_PATCH=None
152
153         for a, b in version_dict.iteritems():
154             if a.startswith("SAMBA_VERSION_"):
155                 setattr(self, a[14:], b)
156             else:
157                 setattr(self, a, b)
158
159         if self.IS_GIT_SNAPSHOT == "yes":
160             self.IS_SNAPSHOT=True
161         elif self.IS_GIT_SNAPSHOT == "no":
162             self.IS_SNAPSHOT=False
163         else:
164             raise Exception("Unknown value for IS_GIT_SNAPSHOT: %s" % self.IS_GIT_SNAPSHOT)
165
166  ##
167  ## start with "3.0.22"
168  ##
169         self.MAJOR=int(self.MAJOR)
170         self.MINOR=int(self.MINOR)
171         self.RELEASE=int(self.RELEASE)
172
173         SAMBA_VERSION_STRING = ("%u.%u.%u" % (self.MAJOR, self.MINOR, self.RELEASE))
174
175 ##
176 ## maybe add "3.0.22a" or "4.0.0tp11" or "4.0.0alpha1" or "4.0.0beta1" or "3.0.22pre1" or "3.0.22rc1"
177 ## We do not do pre or rc version on patch/letter releases
178 ##
179         if self.REVISION is not None:
180             SAMBA_VERSION_STRING += self.REVISION
181         if self.TP_RELEASE is not None:
182             self.TP_RELEASE = int(self.TP_RELEASE)
183             SAMBA_VERSION_STRING += "tp%u" % self.TP_RELEASE
184         if self.ALPHA_RELEASE is not None:
185             self.ALPHA_RELEASE = int(self.ALPHA_RELEASE)
186             SAMBA_VERSION_STRING += ("alpha%u" % self.ALPHA_RELEASE)
187         if self.BETA_RELEASE is not None:
188             self.BETA_RELEASE = int(self.BETA_RELEASE)
189             SAMBA_VERSION_STRING += ("beta%u" % self.BETA_RELEASE)
190         if self.PRE_RELEASE is not None:
191             self.PRE_RELEASE = int(self.PRE_RELEASE)
192             SAMBA_VERSION_STRING += ("pre%u" % self.PRE_RELEASE)
193         if self.RC_RELEASE is not None:
194             self.RC_RELEASE = int(self.RC_RELEASE)
195             SAMBA_VERSION_STRING += ("rc%u" % self.RC_RELEASE)
196
197         if self.IS_SNAPSHOT:
198             if not is_install:
199                 suffix = "DEVELOPERBUILD"
200                 self.vcs_fields = {}
201             elif os.path.exists(os.path.join(path, ".git")):
202                 suffix, self.vcs_fields = git_version_summary(path, env=env)
203             elif os.path.exists(os.path.join(path, ".bzr")):
204                 suffix, self.vcs_fields = bzr_version_summary(path)
205             elif os.path.exists(os.path.join(path, ".distversion")):
206                 suffix, self.vcs_fields = distversion_version_summary(path)
207             else:
208                 suffix = "UNKNOWN"
209                 self.vcs_fields = {}
210             self.vcs_fields["SUFFIX"] = suffix
211             SAMBA_VERSION_STRING += "-" + suffix
212         else:
213             self.vcs_fields = {}
214
215         self.OFFICIAL_STRING = SAMBA_VERSION_STRING
216
217         if self.VENDOR_SUFFIX is not None:
218             SAMBA_VERSION_STRING += ("-" + self.VENDOR_SUFFIX)
219             self.VENDOR_SUFFIX = self.VENDOR_SUFFIX
220
221             if self.VENDOR_PATCH is not None:
222                 SAMBA_VERSION_STRING += ("-" + self.VENDOR_PATCH)
223                 self.VENDOR_PATCH = self.VENDOR_PATCH
224
225         self.STRING = SAMBA_VERSION_STRING
226
227         if self.RELEASE_NICKNAME is not None:
228             self.STRING_WITH_NICKNAME = "%s (%s)" % (self.STRING, self.RELEASE_NICKNAME)
229         else:
230             self.STRING_WITH_NICKNAME = self.STRING
231
232     def __str__(self):
233         string="/* Autogenerated by waf */\n"
234         string+="#define SAMBA_VERSION_MAJOR %u\n" % self.MAJOR
235         string+="#define SAMBA_VERSION_MINOR %u\n" % self.MINOR
236         string+="#define SAMBA_VERSION_RELEASE %u\n" % self.RELEASE
237         if self.REVISION is not None:
238             string+="#define SAMBA_VERSION_REVISION %u\n" % self.REVISION
239
240         if self.TP_RELEASE is not None:
241             string+="#define SAMBA_VERSION_TP_RELEASE %u\n" % self.TP_RELEASE
242
243         if self.ALPHA_RELEASE is not None:
244             string+="#define SAMBA_VERSION_ALPHA_RELEASE %u\n" % self.ALPHA_RELEASE
245
246         if self.BETA_RELEASE is not None:
247             string+="#define SAMBA_VERSION_BETA_RELEASE %u\n" % self.BETA_RELEASE
248
249         if self.PRE_RELEASE is not None:
250             string+="#define SAMBA_VERSION_PRE_RELEASE %u\n" % self.PRE_RELEASE
251
252         if self.RC_RELEASE is not None:
253             string+="#define SAMBA_VERSION_RC_RELEASE %u\n" % self.RC_RELEASE
254
255         for name in sorted(self.vcs_fields.keys()):
256             string+="#define SAMBA_VERSION_%s " % name
257             value = self.vcs_fields[name]
258             if isinstance(value, basestring):
259                 string += "\"%s\"" % value
260             elif type(value) is int:
261                 string += "%d" % value
262             else:
263                 raise Exception("Unknown type for %s: %r" % (name, value))
264             string += "\n"
265
266         string+="#define SAMBA_VERSION_OFFICIAL_STRING \"" + self.OFFICIAL_STRING + "\"\n"
267
268         if self.VENDOR_SUFFIX is not None:
269             string+="#define SAMBA_VERSION_VENDOR_SUFFIX " + self.VENDOR_SUFFIX + "\n"
270             if self.VENDOR_PATCH is not None:
271                 string+="#define SAMBA_VERSION_VENDOR_PATCH " + self.VENDOR_PATCH + "\n"
272
273         if self.RELEASE_NICKNAME is not None:
274             string+="#define SAMBA_VERSION_RELEASE_NICKNAME " + self.RELEASE_NICKNAME + "\n"
275
276         # We need to put this #ifdef in to the headers so that vendors can override the version with a function
277         string+='''
278 #ifdef SAMBA_VERSION_VENDOR_FUNCTION
279 #  define SAMBA_VERSION_STRING SAMBA_VERSION_VENDOR_FUNCTION
280 #else /* SAMBA_VERSION_VENDOR_FUNCTION */
281 #  define SAMBA_VERSION_STRING "''' + self.STRING_WITH_NICKNAME + '''"
282 #endif
283 '''
284         string+="/* Version for mkrelease.sh: \nSAMBA_VERSION_STRING=" + self.STRING_WITH_NICKNAME + "\n */\n"
285
286         return string
287
288
289 def samba_version_file(version_file, path, env=None, is_install=True):
290     '''Parse the version information from a VERSION file'''
291
292     f = open(version_file, 'r')
293     version_dict = {}
294     for line in f:
295         line = line.strip()
296         if line == '':
297             continue
298         if line.startswith("#"):
299             continue
300         try:
301             split_line = line.split("=")
302             if split_line[1] != "":
303                 value = split_line[1].strip('"')
304                 version_dict[split_line[0]] = value
305         except:
306             print("Failed to parse line %s from %s" % (line, version_file))
307             raise
308
309     return SambaVersion(version_dict, path, env=env, is_install=is_install)
310
311
312
313 def load_version(env=None, is_install=True):
314     '''load samba versions either from ./VERSION or git
315     return a version object for detailed breakdown'''
316     if not env:
317         env = samba_utils.LOAD_ENVIRONMENT()
318
319     version = samba_version_file("./VERSION", ".", env, is_install=is_install)
320     Utils.g_module.VERSION = version.STRING
321     return version