# 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/. from datetime import datetime import io import os import sys from mozbuild.preprocessor import Preprocessor import buildconfig 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:])