# Copyright (c) 2008 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import fnmatch import os import re import shutil import subprocess import utils import SCons.Node Import('env') env = env.Clone() if env['OS'] in ['win32', 'wince']: env.Append(DATE = 'echo %DATE%.%TIME%') else: env.Append(DATE = 'date') def GetInputs(var): return utils.GetInputs(var, env) def Shell(cmd): """Execute a shell command and return the output.""" cmd[0] = env.Entry(cmd[0]).abspath cmd = env.subst(cmd) return subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE).communicate()[0] if env['OS'] == 'win32': def GGUIDGen(value): """Generate a GGUID for the given value.""" return Shell(['$GGUIDGEN', '$NAMESPACE_GUID', value + '-$VERSION']) env.Replace( GGUIDGEN = '#/$OPEN_DIR/tools/gguidgen.exe', NAMESPACE_GUID = '36F65206-5D4E-4752-9D52-27708E10DA79', # MSI version numbers must have the form ... To meet this, # we combine our build and patch version numbers like so: # MSI_VERSION = ... # Note: This assumes that the BUILD and PATCH variables adhere to the range # requirements in version.mk. See comments in version.mk for more details. MSI_BUILD = eval(env.subst('$BUILD * 100 + $PATCH')), MSI_VERSION = '${MAJOR}.${MINOR}.${MSI_BUILD}', ) # Building wxiobjs with candle env.Replace( CANDLEDEFPREFIX = '-d', CANDLEDEFSUFFIX = '', _CANDLEDEFFLAGS = ('${_defines(CANDLEDEFPREFIX, CANDLEDEFINES, ' 'CANDLEDEFSUFFIX, __env__)}'), CANDLECOM = 'candle.exe -out $TARGET $SOURCE ${_CANDLEDEFFLAGS}', ) env.Append( # Note: Since light.exe is run from $OPEN_DIR, candle.exe must generate # output with paths relative to that dir. SCONS_DIR = '..', # the scons dir relative to OPEN_DIR # You can change the names of ProductId vars, but NEVER change their values! CANDLEDEFINES = [ ('OurWin32ProductId', GGUIDGen('OUR_PRODUCT_ID')), ('OurComponentGUID_FFComponentsDirFiles', GGUIDGen('OUR_COMPONENT_GUID_FF_COMPONENTS_DIR_FILES')), ('OurComponentGUID_FFContentDirFiles', GGUIDGen('OUR_COMPONENT_GUID_FF_CONTENT_DIR_FILES')), ('OurComponentGUID_FFDirFiles', GGUIDGen('OUR_COMPONENT_GUID_FF_DIR_FILES')), ('OurComponentGUID_FFLibDirFiles', GGUIDGen('OUR_COMPONENT_GUID_FF_LIB_DIR_FILES')), ('OurComponentGUID_FFRegistry', GGUIDGen('OUR_COMPONENT_GUID_FF_REGISTRY')), ('OurComponentGUID_IEFiles', GGUIDGen('OUR_COMPONENT_GUID_IE_FILES')), ('OurComponentGUID_IERegistry', GGUIDGen('OUR_COMPONENT_GUID_IE_REGISTRY')), ('OurComponentGUID_SharedFiles', GGUIDGen('OUR_COMPONENT_GUID_SHARED_FILES')), ('OurComponentGUID_SharedVersionedFiles', GGUIDGen('OUR_COMPONENT_GUID_SHARED_VERSIONED_FILES')), ('OurComponentGUID_SharedRegistry', GGUIDGen('OUR_COMPONENT_GUID_SHARED_REGISTRY')), ('OurNpapiProductId', GGUIDGen('OUR_2ND_PRODUCT_ID')), ('OurComponentGUID_NpapiFiles', GGUIDGen('OUR_COMPONENT_GUID_NPAPI_FILES')), ('OurComponentGUID_NpapiRegistry', GGUIDGen('OUR_COMPONENT_GUID_NPAPI_REGISTRY')), ('OurMsiVersion', '$MSI_VERSION'), ('OurCommonPath', '$COMMON_OUTDIR'), ('OurIEPath', '$IE_OUTDIR'), ('OurIpcTestPath', '$COMMON_OUTDIR'), ('OurFFPath', '$INSTALLER_OUTDIR/$INSTALLER_BASENAME'), ('OurNpapiPath', '$NPAPI_OUTDIR'), ] ) wix_langs = [re.sub('-', '_', lang) for lang in env['I18N_LANGS']] env.Append( CANDLEDEFINES = [('OurComponentGUID_FFLang' + lang + 'DirFiles', GGUIDGen('OUR_COMPONENT_GUID_FF_' + lang + '_DIR_FILES')) for lang in wix_langs], ) def SafeMkdir(dir): """Like the builtin Mkdir, but doesn't fail if the dir exists.""" def Func(target, source, env): dir_subst = env.subst(dir, target=target) if not os.path.exists(dir_subst): os.makedirs(dir_subst) return 0 return Action(Func, 'SafeMkdir("' + dir + '")') def RecursiveDelete(pattern): """Recursively deletes directories matching a pattern.""" def Func(target, source, env): # strip off '.dir' suffix target_dir = env.subst('${TARGET.base}', target=target) for root, dirs, files in os.walk(target_dir): if fnmatch.fnmatch(os.path.normpath(root), pattern): print 'Deleting', root shutil.rmtree(root) return 0 return Action(Func, 'RecursiveDelete("' + pattern + '")') def ToUnixPath(path): """Converts windows-style \ to unix-style /.""" return re.sub(r'\\', r'/', path) def DirBuilder(env, dirtarget, dirsrcs): """Builder that makes a directory tree by copying source files to corresponding locations inside 'dirtarget'. 'dirsrcs' specifies the list of mappings from source file/directory to the target location. It's formatted like: (, ) Note: source files that come from an output directory must be explicitly specified relative to the toplevel dir '#'. Note: as shorthand, if the target ends with a '/', then the sources will be placed into that dir. Otherwise, source is renamed into the target. """ srcs = [] actions = [Delete('${TARGET.base}')] for target, sources in dirsrcs: target_is_dir = target.endswith('/') if target_is_dir: actions.append(SafeMkdir('${TARGET.base}/' + target)) else: actions.append(SafeMkdir('${TARGET.base}/' + os.path.dirname(target))) for source in env.Flatten(sources): source = env.subst(source, conv=lambda x:x) srcs.append(source) # Special-case for Nodes and Node lists: use their absolute paths for # the Copy() action, otherwise it will be relative to our variant dir # (not what Copy expects). if isinstance(source, list): source = source[0] if isinstance(source, SCons.Node.Node): source = source.abspath # HACK: Compensate for the workaround below. We want the .dir file # to be the dependency to the Command() builder, but we want to copy # the actual directory - so strip the extension here. if source.endswith('.dir'): source = source[:-4] if target_is_dir: actions.append( Copy('${TARGET.base}/' + target + os.path.basename(source), source)) else: actions.append(Copy('${TARGET.base}/' + target, source)) # Remove any .svn directories that were copied. actions.append(RecursiveDelete('*/.svn')) # HACK: Workaround for bug in scons where directories aren't checked for # dependency changes. Instead, we make a temp file the target, and ensure # that that file changes everytime we execute these actions. # See http://scons.tigris.org/issues/show_bug.cgi?id=2261 actions += ['$DATE > ${TARGET}'] return env.Command(env.subst(dirtarget) + '.dir', srcs, actions) env.AddMethod(DirBuilder) def FirefoxInstaller(): dirsrcs = [ ('/', ['$FF3_OUTDIR/genfiles/install.rdf', '$FF3_OUTDIR/genfiles/chrome.manifest']), ('lib/', ['$OPEN_DIR/base/firefox/static_files/lib/updater.js']), ('chrome/chromeFiles/content/', GetInputs('$FF3_RESOURCES $COMMON_RESOURCES')), ('chrome/chromeFiles/locale', ['$FF3_OUTDIR/genfiles/i18n']), ('components/', ['$FF3_MODULE_TYPELIB', '$OPEN_DIR/base/firefox/static_files/components/bootstrap.js']), ('components/${SHLIBPREFIX}gears${SHLIBSUFFIX}', ['$FF2_MODULE']), ('components/${SHLIBPREFIX}gears_ff2${SHLIBSUFFIX}', ['$FF3_MODULE']), ] if env['USING_CCTESTS']: dirsrcs += [ ('components/', ['$IPC_TEST_EXE']), ] if env['OS'] != 'win32': # TODO(playmobil): Inspector should be located in extensions dir on win32. dirsrcs += [ ('resources/inspector', [env.Dir('#/$OPEN_DIR/inspector')]), ('resources/inspector/common/', ['$OPEN_DIR/sdk/gears_init.js', '$OPEN_DIR/sdk/samples/sample.js']), ] if env['MODE'] == 'dbg' and env['OS'] in ['win32', 'wince']: dirsrcs += [ ('components/gears_ff2.pdb', ['$FF2_MODULE_PDB']), ('components/gears.pdb', ['$FF3_MODULE_PDB']), ] if env['OS'] == 'osx': dirsrcs += [ ('resources/', ['$OSX_LAUNCHURL_EXE']), ] dir = env.DirBuilder('$INSTALLER_OUTDIR/$INSTALLER_BASENAME', dirsrcs) actions = [ # Mark files writeable to allow .xpi rebuilds 'chmod -R 777 ${SOURCE.base}', '(cd ${SOURCE.base} && zip -r ../${TARGET.file} .)' ] return env.Command('$FF_XPI', dir, actions) firefox_installer = FirefoxInstaller() def Win32Installer(): wxiobj = env.Command( '$COMMON_GENFILES_DIR/win32_msi.wxiobj', '$COMMON_GENFILES_DIR/win32_msi.wxs', '$CANDLECOM') # TODO(mpcomplete): remove this if/when the notifier goes away. This # creates fake targets to satisfy the installer build. notifier = env.Command( [ '$COMMON_OUTDIR/notifier.exe', '$COMMON_OUTDIR/notifier.dll', '$COMMON_OUTDIR/notifier_test.exe' ], [], 'touch $TARGETS') # light.exe must be run from $OPEN_DIR msi = env.Command( '$WIN32_INSTALLER_MSI', [wxiobj, notifier, firefox_installer, '$IE_MODULE', '$NPAPI_MODULE'], 'cd $OPEN_DIR && light.exe -out ${TARGET.abspath} ${SOURCES[0].abspath}') return msi win32_installer = Win32Installer() def WinCEInstaller(): env['ToUnixPath'] = ToUnixPath inf_outdir = ToUnixPath(env.subst('$IE_OUTDIR')) inf = env.Command( '$COMMON_GENFILES_DIR/wince_cab_fixed.inf', '$COMMON_GENFILES_DIR/wince_cab_ie.inf', 'sed -e "s#bin-....wince-arm.ie.#' + inf_outdir + '#g" $SOURCE > $TARGET') cab = env.Command( '$WINCE_INSTALLER_CAB', [inf, '$IE_MODULE', '$IE_WINCE_SETUP_DLL'], ['cabwiz ${ToUnixPath(str(SOURCE))} /compress' ' /err ${SOURCES[0].base}.log', Copy('$TARGET', '${SOURCE.base}.CAB')]) return cab wince_installer = WinCEInstaller() def SafariPluginBundle(): """This is the actual gears plugin bundle for Safari.""" dirsrcs = [ ('Contents/', ['$SF_OUTDIR/genfiles/Info.plist']), ('Contents/Resources/English.lproj/InfoPlist.strings', ['$OPEN_DIR/tools/osx/English.lproj/InfoPlist.strings']), ('Contents/Resources/', env.Glob('#/$OPEN_DIR/ui/safari/*.nib')), ('Contents/Resources/', ['$CRASH_SENDER_EXE']), ('Contents/Resources/', ['$OSX_CRASH_INSPECTOR_EXE']), ('Contents/Resources/', ['$OSX_LAUNCHURL_EXE']), ('Contents/MacOS/', ['$SF_MODULE']), ] if env['USING_CCTESTS']: dirsrcs += [ ('Contents/Resources/', ['$IPC_TEST_EXE']), ] return env.DirBuilder('$SF_PLUGIN_BUNDLE', dirsrcs) safari_plugin_bundle = SafariPluginBundle() def SafariPluginProxyBundle(): """This is a proxy plugin which simply loads gears into Safari and keeps it in memory. It exists so that gears doesn't unload when Safari wants us to, since that causes crashes.""" dirsrcs = [ ('Contents/', ['$SF_OUTDIR/genfiles/Info.plist']), ('Contents/MacOS/${SHLIBPREFIX}gears${SHLIBSUFFIX}', ['$SF_PROXY_DLL']), ('Contents/Resources/', [safari_plugin_bundle]), ('Contents/Resources/', ['$OPEN_DIR/tools/osx/uninstall.command']), ] return env.DirBuilder('$SF_PLUGIN_PROXY_BUNDLE', dirsrcs) safari_plugin_proxy_bundle = SafariPluginProxyBundle() def SafariInstallerPluginBundle(): dirsrcs = [ ('Contents/Info.plist', ['$OPEN_DIR/base/safari/advanced_stats_sheet.plist']), ('Contents/MacOS/InstallerPlugin', ['$SF_INSTALLER_PLUGIN_EXE']), ('Contents/Resources/AdvancedStatsSheet.nib', [env.Dir('#/$OPEN_DIR/base/safari/advanced_stats_sheet.nib')]), ] return env.DirBuilder('$SF_INSTALLER_PLUGIN_BUNDLE', dirsrcs) safari_installer_plugin_bundle = SafariInstallerPluginBundle() def SafariInputManagerBundle(): info = env.Command('$SF_OUTDIR/genfiles/Enabler-Info.plist', '$OPEN_DIR/tools/osx/Enabler-Info.plist', 'cat $SOURCE |' 'sed \'s/$${EXECUTABLE_NAME}/GearsEnabler/\' |' 'sed \'s/$${PRODUCT_NAME}/GearsEnabler/\' > $TARGET') dirsrcs = [ ('GearsEnabler.bundle/Contents/Info.plist', [info]), ('GearsEnabler.bundle/Contents/MacOS/', ['$SF_INPUTMANAGER_EXE']), ('GearsEnabler.bundle/Contents/Resources/English.lproj/', ['$OPEN_DIR/tools/osx/English.lproj/InfoPlist.strings']), ('Info', ['$OPEN_DIR/tools/osx/Info']), ] return env.DirBuilder('$SF_INPUTMANAGER_BUNDLE', dirsrcs) safari_input_manager_bundle = SafariInputManagerBundle() def SafariInstallerPackage(): pkg = env.Iceberg(env.Dir('${SF_INSTALLER_PKG}'), [ '$SF_OUTDIR/genfiles/installer.packproj', safari_plugin_proxy_bundle, safari_input_manager_bundle, ]) return pkg safari_installer_package = SafariInstallerPackage() def SafariKeystoneInstaller(): if not os.path.exists(env.Dir('#/$PRIVATE_DIR').abspath): print 'Skipping Safari Keystone installer. Required sources are not public.' return [] env.Append(CREATE_DISK_IMAGE = "/usr/bin/hdiutil create -ov -imagekey zlib-level=9 -fs HFS+" " -format UDZO -volname '$FRIENDLY_NAME ${VERSION}'" " -srcfolder '${INSTALLER_OUTDIR}/Safari/dmg/' -scrub" " -nocrossdev '${SF_KEYSTONE_INSTALLER_DMG}'" ) pkg = env.Iceberg(env.Dir('${SF_KEYSTONE_INSTALLER_MPKG}'), ['$SF_OUTDIR/genfiles/keystone_installer.packproj']) env.Depends(pkg, GetInputs('$SF_M4S')) env.Depends(pkg, safari_installer_package) dirsrcs = [ ('/', [pkg]), ('/.keystone_install', ['$PRIVATE_DIR/tools/osx/installer/keystone_install']), ] dmg = env.DirBuilder('$INSTALLER_OUTDIR/Safari/dmg', dirsrcs) env.AddPostAction(dmg, 'chmod +x ${TARGET.base}/.keystone_install') # hdiutil is crashy under leopard, so try twice. env.AddPostAction(dmg, '$CREATE_DISK_IMAGE || $CREATE_DISK_IMAGE') return dmg safari_keystone_installer = SafariKeystoneInstaller() installers = [] if 'FF3' in env['VALID_BROWSERS']: installers += firefox_installer if 'SF' in env['VALID_BROWSERS']: installers += [ safari_input_manager_bundle, safari_plugin_bundle, safari_plugin_proxy_bundle, safari_installer_plugin_bundle, safari_installer_package, safari_input_manager_bundle, safari_keystone_installer, ] if env['OS'] == 'win32': installers += win32_installer if env['OS'] == 'wince': installers += wince_installer env.Alias('gears-installers', installers)