WHATSNEW: Remove over-caution on s3fs and explain browsing better
[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                 string+='#define SAMBA_VERSION_VENDOR_PATCH_STRING "' + self.VENDOR_PATCH + '"\n'
273
274         if self.RELEASE_NICKNAME is not None:
275             string+="#define SAMBA_VERSION_RELEASE_NICKNAME " + self.RELEASE_NICKNAME + "\n"
276
277         # We need to put this #ifdef in to the headers so that vendors can override the version with a function
278         string+='''
279 #ifdef SAMBA_VERSION_VENDOR_FUNCTION
280 #  define SAMBA_VERSION_STRING SAMBA_VERSION_VENDOR_FUNCTION
281 #else /* SAMBA_VERSION_VENDOR_FUNCTION */
282 #  define SAMBA_VERSION_STRING "''' + self.STRING_WITH_NICKNAME + '''"
283 #endif
284 '''
285         string+="/* Version for mkrelease.sh: \nSAMBA_VERSION_STRING=" + self.STRING_WITH_NICKNAME + "\n */\n"
286
287         return string
288
289
290 def samba_version_file(version_file, path, env=None, is_install=True):
291     '''Parse the version information from a VERSION file'''
292
293     f = open(version_file, 'r')
294     version_dict = {}
295     for line in f:
296         line = line.strip()
297         if line == '':
298             continue
299         if line.startswith("#"):
300             continue
301         try:
302             split_line = line.split("=")
303             if split_line[1] != "":
304                 value = split_line[1].strip('"')
305                 version_dict[split_line[0]] = value
306         except:
307             print("Failed to parse line %s from %s" % (line, version_file))
308             raise
309
310     return SambaVersion(version_dict, path, env=env, is_install=is_install)
311
312
313
314 def load_version(env=None, is_install=True):
315     '''load samba versions either from ./VERSION or git
316     return a version object for detailed breakdown'''
317     if not env:
318         env = samba_utils.LOAD_ENVIRONMENT()
319
320     version = samba_version_file("./VERSION", ".", env, is_install=is_install)
321     Utils.g_module.VERSION = version.STRING
322     return version