Bug 1905865 - [devtools] Update webidl-pure-allowlist.js. r=devtools-reviewers,ochameau.
[gecko.git] / config / create_rc.py
blob73ce559fd83e211fbbb4bddeaa1021b1663f4e1b
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 import io
6 import os
7 import sys
8 from datetime import datetime
10 import buildconfig
11 from mozbuild.preprocessor import Preprocessor
12 from variables import get_buildid
14 TEMPLATE = """
15 // This Source Code Form is subject to the terms of the Mozilla Public
16 // License, v. 2.0. If a copy of the MPL was not distributed with this
17 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
19 #include<winuser.h>
20 #include<winver.h>
22 // Note: if you contain versioning information in an included
23 // RC script, it will be discarded
24 // Use module.ver to explicitly set these values
26 // Do not edit this file. Changes won't affect the build.
28 {include}
30 Identity LimitedAccessFeature {{ L"{lafidentity}_pcsmm0jrprpb2" }}
33 /////////////////////////////////////////////////////////////////////////////
35 // Version
38 1 VERSIONINFO
39 FILEVERSION {fileversion}
40 PRODUCTVERSION {productversion}
41 FILEFLAGSMASK 0x3fL
42 FILEFLAGS {fileflags}
43 FILEOS VOS__WINDOWS32
44 FILETYPE VFT_DLL
45 FILESUBTYPE 0x0L
46 BEGIN
47 BLOCK "StringFileInfo"
48 BEGIN
49 BLOCK "000004b0"
50 BEGIN
51 VALUE "Comments", "{comment}"
52 VALUE "LegalCopyright", "{copyright}"
53 VALUE "CompanyName", "{company}"
54 VALUE "FileDescription", "{description}"
55 VALUE "FileVersion", "{mfversion}"
56 VALUE "ProductVersion", "{mpversion}"
57 VALUE "InternalName", "{module}"
58 VALUE "LegalTrademarks", "{trademarks}"
59 VALUE "OriginalFilename", "{binary}"
60 VALUE "ProductName", "{productname}"
61 VALUE "BuildID", "{buildid}"
62 END
63 END
64 BLOCK "VarFileInfo"
65 BEGIN
66 VALUE "Translation", 0x0, 1200
67 END
68 END
70 """
73 class SystemClockDiscrepancy(Exception):
74 """Represents an error encountered during the build when determining delta between the build time and
75 the commit time of milestone.txt via VCS."""
78 def preprocess(path, defines):
79 pp = Preprocessor(defines=defines, marker="%")
80 pp.context.update(defines)
81 pp.out = io.StringIO()
82 pp.do_filter("substitution")
83 pp.do_include(io.open(path, "r", encoding="latin1"))
84 pp.out.seek(0)
85 return pp.out
88 def parse_module_ver(path, defines):
89 result = {}
90 for line in preprocess(path, defines):
91 content, *comment = line.split("#", 1)
92 if not content.strip():
93 continue
94 entry, value = content.split("=", 1)
95 result[entry.strip()] = value.strip()
96 return result
99 def last_winversion_segment(buildid, app_version_display):
101 The last segment needs to fit into a 16 bit number. We also need to
102 encode what channel this version is from. We'll do this by using 2 bits
103 to encode the channel, and 14 bits to encode the number of hours since
104 the 'config/milestone.txt' was modified (relative to the build time).
106 This gives us about ~682 days of release hours that will yield a unique
107 file version for a specific channel/milestone combination. This should suffice
108 since the main problem we're trying to address is uniqueness in CI for a
109 channel/milestone over about a 1 month period.
111 If two builds for the same channel/milestone are done in CI within the same
112 hour there's still a chance for overlap and issues with AV as originally
113 reported in https://bugzilla.mozilla.org/show_bug.cgi?id=1872242
115 If a build is done after the ~682 day window of uniqueness, the value for
116 this segment will always be the maximum value for the channel (no overflow).
117 It will also always be the maximum value for the channel if a build is done
118 from a source distribution, because we cannot determine the milestone date
119 change without a VCS.
121 If necessary, you can decode the result of this function. You just need to
122 do integer division and divide it by 4. The quotient will be the delta
123 between the milestone bump and the build time, and the remainder will be
124 the channel digit. Refer to the if/else chain near the end of the function
125 for what channels the channel digits map to.
127 Example:
128 Encoded: 1544
130 1554 / 4 =
131 Quotient: 388
132 Remainder: 2 (ESR)
134 from mozversioncontrol import MissingVCSTool, get_repository_object
136 # Max 16 bit value with 2 most significant bits as 0 (reserved so we can
137 # shift later and make room for the channel digit).
138 MAX_VALUE = 0x3FFF
140 try:
141 import time
142 from datetime import timedelta, timezone
143 from pathlib import Path
145 topsrcdir = buildconfig.topsrcdir
146 repo = get_repository_object(topsrcdir)
148 milestone_time = repo.get_last_modified_time_for_file(
149 Path(topsrcdir) / "config" / "milestone.txt"
151 # The buildid doesn't include timezone info, but the milestone_time does.
152 # We're building on this machine, so we just need the system local timezone
153 # added to a buildid constructed datetime object to make a valid comparison.
154 local_tz = timezone(timedelta(seconds=time.timezone))
155 buildid_time = datetime.strptime(buildid, "%Y%m%d%H%M%S").replace(
156 tzinfo=local_tz
159 time_delta = buildid_time - milestone_time
160 # If the time delta is negative it means that the system clock on the build machine is
161 # significantly far ahead. If we're in CI we'll raise an error, since this number mostly
162 # only matters for doing releases in CI. If we're not in CI, we'll just set the value to
163 # the maximum instead of needlessly interrupting the build of a user with fast/intentionally
164 # modified system clock.
165 if time_delta.total_seconds() < 0:
166 if "MOZ_AUTOMATION" in os.environ:
167 raise SystemClockDiscrepancy(
168 f"The system clock is ahead of the milestone.txt commit time "
169 f"by at least {int(time_delta.total_seconds())} seconds (Since "
170 f"the milestone commit must come before the build starts). This "
171 f"is a problem because use a relative time difference to determine the"
172 f"file_version (and it can't be negative), so we cannot proceed. \n\n"
173 f"Please ensure the system clock is correct."
175 else:
176 hours_from_milestone_date = MAX_VALUE
177 else:
178 # Convert from seconds to hours
179 # When a build is done more than ~682 days in the future, we can't represent the value.
180 # We'll always set the value to the maximum value instead of overflowing.
181 hours_from_milestone_date = min(
182 int(time_delta.total_seconds() / 3600), MAX_VALUE
184 except MissingVCSTool:
185 # If we're here we can't use the VCS to determine the time differential, so
186 # we'll just set it to the maximum value instead of doing something weird.
187 hours_from_milestone_date = MAX_VALUE
188 pass
190 if buildconfig.substs.get("NIGHTLY_BUILD"):
191 # Nightly
192 channel_digit = 0
193 elif "b" in app_version_display:
194 # Beta
195 channel_digit = 1
196 elif buildconfig.substs.get("MOZ_ESR"):
197 # ESR
198 channel_digit = 2
199 else:
200 # Release
201 channel_digit = 3
202 # left shift to make room to encode the channel digit
203 return str((hours_from_milestone_date << 2) + channel_digit)
206 def digits_only(s):
207 for l in range(len(s), 0, -1):
208 if s[:l].isdigit():
209 return s[:l]
210 return "0"
213 def split_and_normalize_version(version, len):
214 return ([digits_only(x) for x in version.split(".")] + ["0"] * len)[:len]
217 def has_manifest(module_rc, manifest_id):
218 for lineFromInput in module_rc.splitlines():
219 line = lineFromInput.split(None, 2)
220 if len(line) < 2:
221 continue
222 id, what, *rest = line
223 if id == manifest_id and what in ("24", "RT_MANIFEST"):
224 return True
225 return False
228 def generate_module_rc(binary="", rcinclude=None):
229 deps = set()
230 buildid = get_buildid()
231 milestone = buildconfig.substs["GRE_MILESTONE"]
232 app_version = buildconfig.substs.get("MOZ_APP_VERSION") or milestone
233 app_version_display = buildconfig.substs.get("MOZ_APP_VERSION_DISPLAY")
234 app_winversion = ",".join(split_and_normalize_version(app_version, 4))
235 milestone_winversion = ",".join(
236 split_and_normalize_version(milestone, 3)
237 + [last_winversion_segment(buildid, app_version_display)]
239 display_name = buildconfig.substs.get("MOZ_APP_DISPLAYNAME", "Mozilla")
241 milestone_string = milestone
243 flags = ["0"]
244 if buildconfig.substs.get("MOZ_DEBUG"):
245 flags.append("VS_FF_DEBUG")
246 milestone_string += " Debug"
247 if not buildconfig.substs.get("MOZILLA_OFFICIAL"):
248 flags.append("VS_FF_PRIVATEBUILD")
249 if buildconfig.substs.get("NIGHTLY_BUILD"):
250 flags.append("VS_FF_PRERELEASE")
252 defines = {
253 "MOZ_APP_DISPLAYNAME": display_name,
254 "MOZ_APP_VERSION": app_version,
255 "MOZ_APP_WINVERSION": app_winversion,
258 relobjdir = os.path.relpath(".", buildconfig.topobjdir)
259 srcdir = os.path.join(buildconfig.topsrcdir, relobjdir)
260 module_ver = os.path.join(srcdir, "module.ver")
261 if os.path.exists(module_ver):
262 deps.add(module_ver)
263 overrides = parse_module_ver(module_ver, defines)
264 else:
265 overrides = {}
267 if rcinclude:
268 include = "// From included resource {}\n{}".format(
269 rcinclude, preprocess(rcinclude, defines).read()
271 else:
272 include = ""
274 # Set the identity field for the Limited Access Feature
275 # Must match the tokens used in Win11LimitedAccessFeatures.cpp
276 lafidentity = "MozillaFirefox"
277 # lafidentity = "FirefoxBeta"
278 # lafidentity = "FirefoxNightly"
280 data = TEMPLATE.format(
281 include=include,
282 lafidentity=lafidentity,
283 fileversion=overrides.get("WIN32_MODULE_FILEVERSION", milestone_winversion),
284 productversion=overrides.get(
285 "WIN32_MODULE_PRODUCTVERSION", milestone_winversion
287 fileflags=" | ".join(flags),
288 comment=overrides.get("WIN32_MODULE_COMMENT", ""),
289 copyright=overrides.get("WIN32_MODULE_COPYRIGHT", "License: MPL 2"),
290 company=overrides.get("WIN32_MODULE_COMPANYNAME", "Mozilla Foundation"),
291 description=overrides.get("WIN32_MODULE_DESCRIPTION", ""),
292 mfversion=overrides.get("WIN32_MODULE_FILEVERSION_STRING", milestone_string),
293 mpversion=overrides.get("WIN32_MODULE_PRODUCTVERSION_STRING", milestone_string),
294 module=overrides.get("WIN32_MODULE_NAME", ""),
295 trademarks=overrides.get("WIN32_MODULE_TRADEMARKS", "Mozilla"),
296 binary=overrides.get("WIN32_MODULE_ORIGINAL_FILENAME", binary),
297 productname=overrides.get("WIN32_MODULE_PRODUCTNAME", display_name),
298 buildid=buildid,
301 manifest_id = "2" if binary.lower().endswith(".dll") else "1"
302 if binary and not has_manifest(data, manifest_id):
303 manifest_path = os.path.join(srcdir, binary + ".manifest")
304 if os.path.exists(manifest_path):
305 manifest_path = manifest_path.replace("\\", "\\\\")
306 data += '\n{} RT_MANIFEST "{}"\n'.format(manifest_id, manifest_path)
308 with io.open("{}.rc".format(binary or "module"), "w", encoding="latin1") as fh:
309 fh.write(data)
312 if __name__ == "__main__":
313 generate_module_rc(*sys.argv[1:])