# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. import io import os import sys from datetime import datetime import buildconfig from mozbuild.preprocessor import Preprocessor TEMPLATE = """ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include #include // Note: if you contain versioning information in an included // RC script, it will be discarded // Use module.ver to explicitly set these values // Do not edit this file. Changes won't affect the build. {include} ///////////////////////////////////////////////////////////////////////////// // // Version // 1 VERSIONINFO FILEVERSION {fileversion} PRODUCTVERSION {productversion} FILEFLAGSMASK 0x3fL FILEFLAGS {fileflags} FILEOS VOS__WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "000004b0" BEGIN VALUE "Comments", "{comment}" VALUE "LegalCopyright", "{copyright}" VALUE "CompanyName", "{company}" VALUE "FileDescription", "{description}" VALUE "FileVersion", "{mfversion}" VALUE "ProductVersion", "{mpversion}" VALUE "InternalName", "{module}" VALUE "LegalTrademarks", "{trademarks}" VALUE "OriginalFilename", "{binary}" VALUE "ProductName", "{productname}" VALUE "BuildID", "{buildid}" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0, 1200 END END """ def preprocess(path, defines): pp = Preprocessor(defines=defines, marker="%") pp.context.update(defines) pp.out = io.StringIO() pp.do_filter("substitution") pp.do_include(io.open(path, "r", encoding="latin1")) pp.out.seek(0) return pp.out def parse_module_ver(path, defines): result = {} for line in preprocess(path, defines): content, *comment = line.split("#", 1) if not content.strip(): continue entry, value = content.split("=", 1) result[entry.strip()] = value.strip() return result def get_buildid(): path = os.path.join(buildconfig.topobjdir, "buildid.h") define, MOZ_BUILDID, buildid = io.open(path, "r", encoding="utf-8").read().split() return buildid def days_from_2000_to_buildid(buildid): start = datetime(2000, 1, 1, 0, 0, 0) buildid_time = datetime.strptime(buildid, "%Y%m%d%H%M%S") return (buildid_time - start).days def digits_only(s): for l in range(len(s), 0, -1): if s[:l].isdigit(): return s[:l] return "0" def split_and_normalize_version(version, len): return ([digits_only(x) for x in version.split(".")] + ["0"] * len)[:len] def has_manifest(module_rc, manifest_id): for line in module_rc.splitlines(): line = line.split(None, 2) if len(line) < 2: continue id, what, *rest = line if id == manifest_id and what in ("24", "RT_MANIFEST"): return True return False def generate_module_rc(binary="", rcinclude=None): deps = set() buildid = get_buildid() milestone = buildconfig.substs["GRE_MILESTONE"] app_version = buildconfig.substs.get("MOZ_APP_VERSION") or milestone app_winversion = ",".join(split_and_normalize_version(app_version, 4)) milestone_winversion = ",".join( split_and_normalize_version(milestone, 3) + [str(days_from_2000_to_buildid(buildid))] ) display_name = buildconfig.substs.get("MOZ_APP_DISPLAYNAME", "Mozilla") milestone_string = milestone flags = ["0"] if buildconfig.substs.get("MOZ_DEBUG"): flags.append("VS_FF_DEBUG") milestone_string += " Debug" if not buildconfig.substs.get("MOZILLA_OFFICIAL"): flags.append("VS_FF_PRIVATEBUILD") if buildconfig.substs.get("NIGHTLY_BUILD"): flags.append("VS_FF_PRERELEASE") defines = { "MOZ_APP_DISPLAYNAME": display_name, "MOZ_APP_VERSION": app_version, "MOZ_APP_WINVERSION": app_winversion, } relobjdir = os.path.relpath(".", buildconfig.topobjdir) srcdir = os.path.join(buildconfig.topsrcdir, relobjdir) module_ver = os.path.join(srcdir, "module.ver") if os.path.exists(module_ver): deps.add(module_ver) overrides = parse_module_ver(module_ver, defines) else: overrides = {} if rcinclude: include = "// From included resource {}\n{}".format( rcinclude, preprocess(rcinclude, defines).read() ) else: include = "" data = TEMPLATE.format( include=include, fileversion=overrides.get("WIN32_MODULE_FILEVERSION", milestone_winversion), productversion=overrides.get( "WIN32_MODULE_PRODUCTVERSION", milestone_winversion ), fileflags=" | ".join(flags), comment=overrides.get("WIN32_MODULE_COMMENT", ""), copyright=overrides.get("WIN32_MODULE_COPYRIGHT", "License: MPL 2"), company=overrides.get("WIN32_MODULE_COMPANYNAME", "Mozilla Foundation"), description=overrides.get("WIN32_MODULE_DESCRIPTION", ""), mfversion=overrides.get("WIN32_MODULE_FILEVERSION_STRING", milestone_string), mpversion=overrides.get("WIN32_MODULE_PRODUCTVERSION_STRING", milestone_string), module=overrides.get("WIN32_MODULE_NAME", ""), trademarks=overrides.get("WIN32_MODULE_TRADEMARKS", "Mozilla"), binary=overrides.get("WIN32_MODULE_ORIGINAL_FILENAME", binary), productname=overrides.get("WIN32_MODULE_PRODUCTNAME", display_name), buildid=buildid, ) manifest_id = "2" if binary.lower().endswith(".dll") else "1" if binary and not has_manifest(data, manifest_id): manifest_path = os.path.join(srcdir, binary + ".manifest") if os.path.exists(manifest_path): manifest_path = manifest_path.replace("\\", "\\\\") data += '\n{} RT_MANIFEST "{}"\n'.format(manifest_id, manifest_path) with io.open("{}.rc".format(binary or "module"), "w", encoding="latin1") as fh: fh.write(data) if __name__ == "__main__": generate_module_rc(*sys.argv[1:])