From b7b4135e5507fd7be5aab24d5de64675dd28caca Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Wed, 21 Feb 2007 23:57:35 +0000 Subject: SL-33966: Viewer installed files are not described in a centralized source This introduces the viewer_manifest.py and associated libraries. Check out https://osiris.lindenlab.com/mediawiki/index.php/Installer_Manifest for some more information. The gist of it is that all files to be included in the viewer installations are described in viewer_manifest.py. viewer_manifest.py also acts as a packager, if you give it the right options. All of the old methods of packaging (Makefile_Mac, scons, and the various .bat files) still work, but they are mostly just shells for calls to viewer_manifest.py. You're now required to have python 2.3 or later on your machine to be able to package up an installer, but thankfully most people already have this. --- .../installers/windows/installer_template.nsi | 858 +++++++++++++++++++++ indra/newview/viewer_manifest.py | 437 +++++++++++ indra/test/test_llmanifest.py | 109 +++ 3 files changed, 1404 insertions(+) create mode 100644 indra/newview/installers/windows/installer_template.nsi create mode 100755 indra/newview/viewer_manifest.py create mode 100644 indra/test/test_llmanifest.py diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi new file mode 100644 index 0000000000..6178cfbc96 --- /dev/null +++ b/indra/newview/installers/windows/installer_template.nsi @@ -0,0 +1,858 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; secondlife setup.nsi +;;; Copyright 2004-2006, Linden Research, Inc. +;;; For info, see http://www.nullsoft.com/free/nsis/ +;;; +;;; NSIS 2.02 or higher required +;;; Author: James Cook, Don Kjer +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Compiler flags +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; Detect NSIS compiler version +!define "NSIS${NSIS_VERSION}" +!ifdef "NSISv2.02" | "NSISv2.03" | "NSISv2.04" | "NSISv2.05" | "NSISv2.06" + ;;; before 2.07 defaulted lzma to solid (whole file) + SetCompressor lzma +!else + ;;; after 2.07 required /solid for whole file compression + SetCompressor /solid lzma +!endif + +SetOverwrite on ; overwrite files +SetCompress auto ; compress iff saves space +SetDatablockOptimize off ; only saves us 0.1%, not worth it +XPStyle on ; add an XP manifest to the installer + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Project flags +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +%%VERSION%% + +;;; Tweak for different servers/builds (this placeholder is replaced by viewer_manifest.py) +%%GRID_VARS%% + +Name ${INSTNAME} + +SubCaption 0 " Setup" ; override "license agreement" text + +BrandingText " " ; bottom of window text +Icon res\install_icon.ico ; our custom icon +UninstallIcon res\uninstall_icon.ico ; our custom icon +WindowIcon on ; show our icon in left corner +BGGradient off ; no big background window +CRCCheck on ; make sure CRC is OK +InstProgressFlags smooth colored ; new colored smooth look +ShowInstDetails nevershow ; no details, no "show" button +SetOverwrite on ; stomp files by default +AutoCloseWindow true ; after all files install, close window + +!ifdef UPDATE +LicenseText "This package will update Second Life to version ${VERSION_LONG}." "Next >" +!else +LicenseText "This package will install Second Life on your computer." "Next >" +!endif + +LicenseData "releasenotes.txt" + +InstallDir "$PROGRAMFILES\${INSTNAME}" +InstallDirRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\${INSTNAME}" "" +!ifdef UPDATE +DirText "Installation Directory" "Select the Second Life directory to update:" +!else +DirText "Installation Directory" "Select the directory to install Second Life in:" +!endif + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Variables +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Var INSTPROG +Var INSTEXE +Var INSTFLAGS +Var INSTSHORTCUT + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Sections +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +Section "" ; (default section) + +SetShellVarContext all ; install for all users (if you change this, change it in the uninstall as well) + + +; Start with some default values. +StrCpy $INSTFLAGS "${INSTFLAGS}" +StrCpy $INSTPROG "${INSTNAME}" +StrCpy $INSTEXE "${INSTEXE}" +StrCpy $INSTSHORTCUT "${SHORTCUT}" + +IfSilent +2 +Goto NOT_SILENT + Call CheckStartupParams ; Figure out where, what and how to install. +NOT_SILENT: +Call CheckWindowsVersion ; warn if on Windows 98/ME +Call CheckIfAdministrator ; Make sure the user can install/uninstall +Call CheckIfAlreadyCurrent ; Make sure that we haven't already installed this version +Call CloseSecondLife ; Make sure we're not running +Call RemoveNSIS ; Check for old NSIS install to remove + +;;; Don't remove cache files during a regular install, removing the inventory cache on upgrades results in lots of damage to the servers. +;Call RemoveCacheFiles ; Installing over removes potentially corrupted + ; VFS and cache files. + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Files +;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; This placeholder is replaced by the complete list of all the files in the installer, by viewer_manifest.py +%%INSTALL_FILES%% + +; If this is a silent update, we don't need to re-create these shortcuts or registry entries. +IfSilent POST_INSTALL + +; Shortcuts in start menu +CreateDirectory "$SMPROGRAMS\$INSTSHORTCUT" +SetOutPath "$INSTDIR" +CreateShortCut "$SMPROGRAMS\$INSTSHORTCUT\$INSTSHORTCUT.lnk" \ + "$INSTDIR\$INSTEXE" "$INSTFLAGS" + +!ifdef MUSEUM +CreateShortCut "$SMPROGRAMS\$INSTSHORTCUT\$INSTSHORTCUT Museum.lnk" \ + + "$INSTDIR\$INSTEXE" "$INSTFLAGS -simple" +CreateShortCut "$SMPROGRAMS\$INSTSHORTCUT\$INSTSHORTCUT Museum Spanish.lnk" \ + + "$INSTDIR\$INSTEXE" "$INSTFLAGS -simple -spanish" +!endif + +WriteINIStr "$SMPROGRAMS\$INSTSHORTCUT\SL Create Trial Account.url" \ + "InternetShortcut" "URL" \ + "http://www.secondlife.com/registration/" +WriteINIStr "$SMPROGRAMS\$INSTSHORTCUT\SL Your Account.url" \ + "InternetShortcut" "URL" \ + "http://www.secondlife.com/account/" +CreateShortCut "$SMPROGRAMS\$INSTSHORTCUT\SL Release Notes.lnk" \ + "$INSTDIR\releasenotes.txt" +CreateShortCut "$SMPROGRAMS\$INSTSHORTCUT\SL Scripting Language Help.lnk" \ + "$INSTDIR\lsl_guide.html" +CreateShortCut "$SMPROGRAMS\$INSTSHORTCUT\Uninstall $INSTSHORTCUT.lnk" \ + '"$INSTDIR\uninst.exe"' '/P="$INSTPROG"' + +; Other shortcuts +SetOutPath "$INSTDIR" +CreateShortCut "$DESKTOP\$INSTSHORTCUT.lnk" "$INSTDIR\$INSTEXE" "$INSTFLAGS" +CreateShortCut "$INSTDIR\$INSTSHORTCUT.lnk" "$INSTDIR\$INSTEXE" "$INSTFLAGS" +CreateShortCut "$INSTDIR\Uninstall $INSTSHORTCUT.lnk" \ + '"$INSTDIR\uninst.exe"' '/P="$INSTPROG"' + +!ifdef MUSEUM +CreateShortCut "$DESKTOP\$INSTSHORTCUT Museum.lnk" "$INSTDIR\$INSTEXE" "$INSTFLAGS -simple" + +CreateShortCut "$DESKTOP\$INSTSHORTCUT Museum Spanish.lnk" "$INSTDIR\$INSTEXE" "$INSTFLAGS -simple -spanish" + +CreateShortCut "$INSTDIR\$INSTSHORTCUT Museum.lnk" "$INSTDIR\$INSTEXE" "$INSTFLAGS -simple" + +CreateShortCut "$INSTDIR\$INSTSHORTCUT Museum Spanish.lnk" "$INSTDIR\$INSTEXE" "$INSTFLAGS -simple -spanish" + +!endif + +; Write registry +WriteRegStr HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "" "$INSTDIR" +WriteRegStr HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Version" "${VERSION_LONG}" +WriteRegStr HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Flags" "$INSTFLAGS" +WriteRegStr HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Shortcut" "$INSTSHORTCUT" +WriteRegStr HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Exe" "$INSTEXE" +WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "DisplayName" "$INSTPROG (remove only)" +WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" "UninstallString" '"$INSTDIR\uninst.exe" /P="$INSTPROG"' + +; Write URL registry info +WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}" "(default)" "URL:Second Life" +WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}" "URL Protocol" "" +WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}\DefaultIcon" "" '"$INSTDIR\$INSTEXE"' +WriteRegExpandStr HKEY_CLASSES_ROOT "${URLNAME}\shell\open\command" "" '"$INSTDIR\$INSTEXE" $INSTFLAGS -url "%1"' + +Goto WRITE_UNINST + +POST_INSTALL: +; Run a post-executable script if necessary. +Call PostInstallExe + +WRITE_UNINST: +; write out uninstaller +WriteUninstaller "$INSTDIR\uninst.exe" + +; end of default section +SectionEnd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; PostInstallExe +; This just runs any post installation scripts. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Function PostInstallExe +push $0 + ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "PostInstallExe" + ;MessageBox MB_OK '$0' + ExecWait '$0' +pop $0 +FunctionEnd + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; CheckStartupParameters +; Sets INSTFLAGS, INSTPROG, and INSTEXE. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Function CheckStartupParams +push $0 +push $R0 + + ; Look for a registry entry with info about where to update. + Call GetProgramName + pop $R0 + StrCpy $INSTPROG "$R0" + StrCpy $INSTEXE "$R0.exe" + + ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "" + ; If key doesn't exist, skip install + IfErrors ABORT + StrCpy $INSTDIR "$0" + + ; We now have a directory to install to. Get the startup parameters and shortcut as well. + ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Flags" + IfErrors +2 + StrCpy $INSTFLAGS "$0" + ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Shortcut" + IfErrors +2 + StrCpy $INSTSHORTCUT "$0" + ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Exe" + IfErrors +2 + StrCpy $INSTEXE "$0" + Goto FINISHED + +ABORT: + MessageBox MB_OK "Could not find the program '$INSTPROG'. Silent update failed." + Quit + +FINISHED: + ;MessageBox MB_OK "INSTPROG: $INSTPROG, INSTEXE: $INSTEXE, INSTFLAGS: $INSTFLAGS" +pop $R0 +pop $0 +FunctionEnd + +Function un.CheckStartupParams +push $0 +push $R0 + + ; Look for a registry entry with info about where to update. + Call un.GetProgramName + pop $R0 + StrCpy $INSTPROG "$R0" + StrCpy $INSTEXE "$R0.exe" + + ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "" + ; If key doesn't exist, skip install + IfErrors ABORT + StrCpy $INSTDIR "$0" + + ; We now have a directory to install to. Get the startup parameters and shortcut as well. + ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Flags" + IfErrors +2 + StrCpy $INSTFLAGS "$0" + ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Shortcut" + IfErrors +2 + StrCpy $INSTSHORTCUT "$0" + ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Exe" + IfErrors +2 + StrCpy $INSTEXE "$0" + Goto FINISHED + +ABORT: + MessageBox MB_OK "Could not find the program '$INSTPROG'. Silent update failed." + Quit + +FINISHED: + ;MessageBox MB_OK "INSTPROG: $INSTPROG, INSTEXE: $INSTEXE, INSTFLAGS: $INSTFLAGS" +pop $R0 +pop $0 +FunctionEnd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; After install completes, offer readme file +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Function .onInstSuccess + MessageBox MB_YESNO \ + "Start Second Life now?" /SD IDYES IDNO NoReadme + ; Assumes SetOutPath $INSTDIR + Exec '"$INSTDIR\$INSTEXE" $INSTFLAGS' + NoReadme: +FunctionEnd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Remove old NSIS version. Modifies no variables. +; Does NOT delete the LindenWorld directory, or any +; user files in that directory. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Function RemoveNSIS + Push $0 + ; Grab the installation directory of the old version + DetailPrint "Checking for old version..." + ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "" + + ; If key doesn't exist, skip uninstall + IfErrors NO_NSIS + + ; Clean up legacy beta shortcuts + Delete "$SMPROGRAMS\Second Life Beta.lnk" + Delete "$DESKTOP\Second Life Beta.lnk" + Delete "$SMPROGRAMS\Second Life.lnk" + + ; Clean up old newview.exe file + Delete "$INSTDIR\newview.exe" + + ; Intentionally don't delete the stuff in + ; Documents and Settings, so we keep the user's settings + + NO_NSIS: + Pop $0 +FunctionEnd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Make sure we're not on Windows 98 / ME +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Function CheckWindowsVersion + DetailPrint "Checking Windows version..." + Call GetWindowsVersion + Pop $R0 + ; Just get first two characters, ignore 4.0 part of "NT 4.0" + StrCpy $R0 $R0 2 + ; Blacklist certain OS versions + StrCmp $R0 "95" win_ver_bad + StrCmp $R0 "98" win_ver_bad + StrCmp $R0 "ME" win_ver_bad + StrCmp $R0 "NT" win_ver_bad + Return +win_ver_bad: + MessageBox MB_YESNO 'Second Life only supports Windows XP, Windows 2000, and Mac OS X.$\n$\nAttempting to install on Windows $R0 can result in crashes and data loss.$\n$\nInstall anyway?' IDNO win_ver_abort + Return +win_ver_abort: + Quit +FunctionEnd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Make sure the user can install/uninstall +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Function CheckIfAdministrator + DetailPrint "Checking for permission to install..." + UserInfo::GetAccountType + Pop $R0 + StrCmp $R0 "Admin" is_admin + MessageBox MB_OK 'You appear to be using a "limited" account.$\nYou must be an "administrator" to install Second Life.' + Quit +is_admin: + Return +FunctionEnd + +Function un.CheckIfAdministrator + DetailPrint "Checking for permission to uninstall..." + UserInfo::GetAccountType + Pop $R0 + StrCmp $R0 "Admin" is_admin + MessageBox MB_OK 'You appear to be using a "limited" account.$\nYou must be an "administrator" to uninstall Second Life.' + Quit +is_admin: + Return +FunctionEnd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Checks to see if the current version has already been installed (according to the registry). +; If it has, allow user to bail out of install process. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Function CheckIfAlreadyCurrent + Push $0 + ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Version" + StrCmp $0 ${VERSION_LONG} 0 DONE + MessageBox MB_OKCANCEL "It appears that Second Life ${VERSION_LONG} is already installed.$\n$\nWould you like to install it again?" /SD IDOK IDOK DONE + Quit + + DONE: + Pop $0 + Return +FunctionEnd + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Close the program, if running. Modifies no variables. +; Allows user to bail out of install process. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Function CloseSecondLife + Push $0 + FindWindow $0 "Second Life" "" + IntCmp $0 0 DONE + MessageBox MB_OKCANCEL "Second Life can't be installed while it is already running.$\n$\nFinish what you're doing then select OK to close Second Life and continue.$\nSelect CANCEL to cancel installation." IDOK CLOSE IDCANCEL CANCEL_INSTALL + + CANCEL_INSTALL: + Quit + + CLOSE: + DetailPrint "Waiting for Second Life to shut down..." + SendMessage $0 16 0 0 + + LOOP: + FindWindow $0 "Second Life" "" + IntCmp $0 0 DONE + Sleep 500 + Goto LOOP + + DONE: + Pop $0 + Return +FunctionEnd + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Delete files in Documents and Settings\\SecondLife\cache +; Delete files in Documents and Settings\All Users\SecondLife\cache +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Function RemoveCacheFiles + +; Delete files in Documents and Settings\\SecondLife +Push $0 +Push $1 +Push $2 + DetailPrint "Deleting cache files in Documents and Settings folder" + + StrCpy $0 0 ; Index number used to iterate via EnumRegKey + + LOOP: + EnumRegKey $1 HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $0 + StrCmp $1 "" DONE ; no more users + + ReadRegStr $2 HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$1" "ProfileImagePath" + StrCmp $2 "" CONTINUE 0 ; "ProfileImagePath" value is missing + + ; Required since ProfileImagePath is of type REG_EXPAND_SZ + ExpandEnvStrings $2 $2 + + ; When explicitly uninstalling, everything goes away + RMDir /r "$2\Application Data\SecondLife\cache" + + CONTINUE: + IntOp $0 $0 + 1 + Goto LOOP + DONE: +Pop $2 +Pop $1 +Pop $0 + +; Delete files in Documents and Settings\All Users\SecondLife +Push $0 + ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" "Common AppData" + StrCmp $0 "" +2 + RMDir /r "$0\SecondLife\cache" +Pop $0 + +; Delete filse in C:\Windows\Application Data\SecondLife +; If the user is running on a pre-NT system, Application Data lives here instead of +; in Documents and Settings. +RMDir /r "$WINDIR\Application Data\SecondLife\cache" + +FunctionEnd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Delete files in Documents and Settings\\SecondLife +; Delete files in Documents and Settings\All Users\SecondLife +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Function un.DocumentsAndSettingsFolder + +; Delete files in Documents and Settings\\SecondLife +Push $0 +Push $1 +Push $2 + + DetailPrint "Deleting files in Documents and Settings folder" + + StrCpy $0 0 ; Index number used to iterate via EnumRegKey + + LOOP: + EnumRegKey $1 HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $0 + StrCmp $1 "" DONE ; no more users + + ReadRegStr $2 HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$1" "ProfileImagePath" + StrCmp $2 "" CONTINUE 0 ; "ProfileImagePath" value is missing + + ; Required since ProfileImagePath is of type REG_EXPAND_SZ + ExpandEnvStrings $2 $2 + + ; If uninstalling a normal install remove everything + ; Otherwise (preview/dmz etc) just remove cache + StrCmp $INSTFLAGS "" RM_ALL RM_CACHE + RM_ALL: + RMDir /r "$2\Application Data\SecondLife" + GoTo CONTINUE + RM_CACHE: + RMDir /r "$2\Application Data\SecondLife\Cache" + + CONTINUE: + IntOp $0 $0 + 1 + Goto LOOP + DONE: + +Pop $2 +Pop $1 +Pop $0 + +; Delete files in Documents and Settings\All Users\SecondLife +Push $0 + ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" "Common AppData" + StrCmp $0 "" +2 + RMDir /r "$0\SecondLife" +Pop $0 + +; Delete filse in C:\Windows\Application Data\SecondLife +; If the user is running on a pre-NT system, Application Data lives here instead of +; in Documents and Settings. +RMDir /r "$WINDIR\Application Data\SecondLife" + +FunctionEnd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Close the program, if running. Modifies no variables. +; Allows user to bail out of uninstall process. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Function un.CloseSecondLife + Push $0 + FindWindow $0 "Second Life" "" + IntCmp $0 0 DONE + MessageBox MB_OKCANCEL "Second Life can't be uninstalled while it is already running.$\n$\nFinish what you're doing then select OK to close Second Life and continue.$\nSelect CANCEL to cancel installation." IDOK CLOSE IDCANCEL CANCEL_UNINSTALL + + CANCEL_UNINSTALL: + Quit + + CLOSE: + DetailPrint "Waiting for Second Life to shut down..." + SendMessage $0 16 0 0 + + LOOP: + FindWindow $0 "Second Life" "" + IntCmp $0 0 DONE + Sleep 500 + Goto LOOP + + DONE: + Pop $0 + Return +FunctionEnd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Delete the installed files +;;; This deletes the uninstall executable, but it works +;;; because it is copied to temp directory before running +;;; +;;; Note: You must list all files here, because we only +;;; want to delete our files, not things users left in the +;;; application directories. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Function un.ProgramFiles + +;; Remove mozilla file first so recursive directory deletion doesn't get hung up +Delete "$INSTDIR\app_settings\mozilla\components" + +;; This placeholder is replaced by the complete list of files to uninstall by viewer_manifest.py +%%DELETE_FILES%% + +;; Optional/obsolete files. Delete won't fail if they don't exist. +Delete "$INSTDIR\dronesettings.ini" +Delete "$INSTDIR\message_template.msg" +Delete "$INSTDIR\newview.pdb" +Delete "$INSTDIR\newview.map" +Delete "$INSTDIR\SecondLife.pdb" +Delete "$INSTDIR\SecondLife.map" +Delete "$INSTDIR\comm.dat" +Delete "$INSTDIR\*.glsl" +Delete "$INSTDIR\motions\*.lla" +Delete "$INSTDIR\trial\*.html" +Delete "$INSTDIR\newview.exe" +;; Remove entire help directory +Delete "$INSTDIR\help\Advanced\*" +RMDir "$INSTDIR\help\Advanced" +Delete "$INSTDIR\help\basics\*" +RMDir "$INSTDIR\help\basics" +Delete "$INSTDIR\help\Concepts\*" +RMDir "$INSTDIR\help\Concepts" +Delete "$INSTDIR\help\welcome\*" +RMDir "$INSTDIR\help\welcome" +Delete "$INSTDIR\help\*" +RMDir "$INSTDIR\help" + +Delete "$INSTDIR\uninst.exe" +RMDir "$INSTDIR" + +IfFileExists "$INSTDIR" FOLDERFOUND NOFOLDER + +FOLDERFOUND: + MessageBox MB_YESNO "There are still files in your SecondLife program directory.$\n$\nThese are possibly files you created or moved to:$\n$INSTDIR$\n$\nDo you want to remove them?" IDNO NOFOLDER + RMDir /r "$INSTDIR" + +NOFOLDER: + +FunctionEnd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Uninstall settings +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +UninstallText "This will uninstall Second Life ${VERSION_LONG} from your system." +ShowUninstDetails show + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Uninstall section +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Section Uninstall + +; Start with some default values. +StrCpy $INSTFLAGS "${FARMFLAG}" +StrCpy $INSTPROG "${INSTNAME}" +StrCpy $INSTEXE "${INSTEXE}" +StrCpy $INSTSHORTCUT "${SHORTCUT}" +Call un.CheckStartupParams ; Figure out where, what and how to uninstall. +Call un.CheckIfAdministrator ; Make sure the user can install/uninstall + +; uninstall for all users (if you change this, change it in the install as well) +SetShellVarContext all + +; Make sure we're not running +Call un.CloseSecondLife + +; Clean up registry keys +DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" +DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$INSTPROG" + +; Clean up shortcuts +Delete "$SMPROGRAMS\$INSTSHORTCUT\*.*" +RMDir "$SMPROGRAMS\$INSTSHORTCUT" + +Delete "$DESKTOP\$INSTSHORTCUT.lnk" +Delete "$INSTDIR\$INSTSHORTCUT.lnk" +Delete "$INSTDIR\Uninstall $INSTSHORTCUT.lnk" + +; Clean up cache and log files. +; Leave them in-place for non AGNI installs. + +!ifdef UNINSTALL_SETTINGS +Call un.DocumentsAndSettingsFolder +!endif + +Call un.ProgramFiles + +SectionEnd ; end of uninstall section + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; (From the NSIS wiki, DK) +; GetParameterValue +; +; Usage: +; !insertmacro GetParameterValue "/L=" "1033" +; pop $R0 +; +; Returns on top of stack +; +; Example command lines: +; foo.exe /S /L=1033 /D=C:\Program Files\Foo +; or: +; foo.exe /S "/L=1033" /D="C:\Program Files\Foo" +; gpv "/L=" "1033" +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + !macro GetParameterValue SWITCH DEFAULT + Push $0 + Push $1 + Push $2 + Push $3 + Push $4 + + ;$CMDLINE='"My Setup\Setup.exe" /L=1033 /S' + Push "$CMDLINE" + Push '${SWITCH}"' + !insertmacro StrStr + Pop $0 + StrCmp "$0" "" gpv_notquoted + ;$0='/L="1033" /S' + StrLen $2 "$0" + Strlen $1 "${SWITCH}" + IntOp $1 $1 + 1 + StrCpy $0 "$0" $2 $1 + ;$0='1033" /S' + Push "$0" + Push '"' + !insertmacro StrStr + Pop $1 + StrLen $2 "$0" + StrLen $3 "$1" + IntOp $4 $2 - $3 + StrCpy $0 $0 $4 0 + Goto gpv_done + + gpv_notquoted: + Push "$CMDLINE" + Push "${SWITCH}" + !insertmacro StrStr + Pop $0 + StrCmp "$0" "" gpv_done + ;$0='/L="1033" /S' + StrLen $2 "$0" + Strlen $1 "${SWITCH}" + StrCpy $0 "$0" $2 $1 + ;$0=1033 /S' + Push "$0" + Push ' ' + !insertmacro StrStr + Pop $1 + StrLen $2 "$0" + StrLen $3 "$1" + IntOp $4 $2 - $3 + StrCpy $0 $0 $4 0 + Goto gpv_done + + gpv_done: + StrCmp "$0" "" 0 +2 + StrCpy $0 "${DEFAULT}" + + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Exch $0 + !macroend + +; And I had to modify StrStr a tiny bit. +; Possible upgrade switch the goto's to use ${__LINE__} + +!macro STRSTR + Exch $R1 ; st=haystack,old$R1, $R1=needle + Exch ; st=old$R1,haystack + Exch $R2 ; st=old$R1,old$R2, $R2=haystack + Push $R3 + Push $R4 + Push $R5 + StrLen $R3 $R1 + StrCpy $R4 0 + ; $R1=needle + ; $R2=haystack + ; $R3=len(needle) + ; $R4=cnt + ; $R5=tmp + ; loop; + StrCpy $R5 $R2 $R3 $R4 + StrCmp $R5 $R1 +4 + StrCmp $R5 "" +3 + IntOp $R4 $R4 + 1 + Goto -4 + ; done; + StrCpy $R1 $R2 "" $R4 + Pop $R5 + Pop $R4 + Pop $R3 + Pop $R2 + Exch $R1 +!macroend + +Function GetProgramName + !insertmacro GetParameterValue "/P=" "SecondLife" +FunctionEnd + +Function un.GetProgramName + !insertmacro GetParameterValue "/P=" "SecondLife" +FunctionEnd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; (From the NSIS documentation, JC) +; GetWindowsVersion +; +; Based on Yazno's function, http://yazno.tripod.com/powerpimpit/ +; Updated by Joost Verburg +; +; Returns on top of stack +; +; Windows Version (95, 98, ME, NT x.x, 2000, XP, 2003) +; or +; '' (Unknown Windows Version) +; +; Usage: +; Call GetWindowsVersion +; Pop $R0 +; ; at this point $R0 is "NT 4.0" or whatnot +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +Function GetWindowsVersion + + Push $R0 + Push $R1 + + ReadRegStr $R0 HKLM \ + "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion + + IfErrors 0 lbl_winnt + + ; we are not NT + ReadRegStr $R0 HKLM \ + "SOFTWARE\Microsoft\Windows\CurrentVersion" VersionNumber + + StrCpy $R1 $R0 1 + StrCmp $R1 '4' 0 lbl_error + + StrCpy $R1 $R0 3 + + StrCmp $R1 '4.0' lbl_win32_95 + StrCmp $R1 '4.9' lbl_win32_ME lbl_win32_98 + + lbl_win32_95: + StrCpy $R0 '95' + Goto lbl_done + + lbl_win32_98: + StrCpy $R0 '98' + Goto lbl_done + + lbl_win32_ME: + StrCpy $R0 'ME' + Goto lbl_done + + lbl_winnt: + + StrCpy $R1 $R0 1 + + StrCmp $R1 '3' lbl_winnt_x + StrCmp $R1 '4' lbl_winnt_x + + StrCpy $R1 $R0 3 + + StrCmp $R1 '5.0' lbl_winnt_2000 + StrCmp $R1 '5.1' lbl_winnt_XP + StrCmp $R1 '5.2' lbl_winnt_2003 lbl_error + + lbl_winnt_x: + StrCpy $R0 "NT $R0" 6 + Goto lbl_done + + lbl_winnt_2000: + Strcpy $R0 '2000' + Goto lbl_done + + lbl_winnt_XP: + Strcpy $R0 'XP' + Goto lbl_done + + lbl_winnt_2003: + Strcpy $R0 '2003' + Goto lbl_done + + lbl_error: + Strcpy $R0 '' + lbl_done: + + Pop $R1 + Exch $R0 + +FunctionEnd diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py new file mode 100755 index 0000000000..104d860c87 --- /dev/null +++ b/indra/newview/viewer_manifest.py @@ -0,0 +1,437 @@ +#!/usr/bin/python +# @file viewer_manifest.py +# @author Ryan Williams +# @brief Description of all installer viewer files, and methods for packaging +# them into installers for all supported platforms. +# +# Copyright (c) 2006-$CurrentYear$, Linden Research, Inc. +# $License$ +import sys +import os.path +import re +import tarfile +viewer_dir = os.path.dirname(__file__) +# add llmanifest library to our path so you don't have to muck with PYTHONPATH +sys.path.append(os.path.join(viewer_dir, '../lib/python/indra')) +from llmanifest import LLManifest, main, proper_windows_path, path_ancestors + +class ViewerManifest(LLManifest): + def construct(self): + super(ViewerManifest, self).construct() + self.exclude("*.svn*") + self.path(src="../../scripts/messages/message_template.msg", dst="app_settings/message_template.msg") + + if self.prefix(src="app_settings"): + self.exclude("logcontrol.xml") + self.exclude("logcontrol-dev.xml") + self.path("*.pem") + self.path("*.ini") + self.path("*.xml") + self.path("*.vp") + self.path("*.db2") + + # include the entire shaders directory recursively + self.path("shaders") + self.end_prefix("app_settings") + + if self.prefix(src="character"): + self.path("*.llm") + self.path("*.xml") + self.path("*.tga") + self.end_prefix("character") + + + # Include our fonts + if self.prefix(src="fonts"): + self.path("*.ttf") + self.path("*.txt") + self.end_prefix("fonts") + + # XUI + if self.prefix(src="skins"): + self.path("paths.xml") + self.path("xui/*/*.xml") + self.path('words.*.txt') + + # Local HTML files (e.g. loading screen) + if self.prefix("html/*"): + self.path("*.html") + self.path("*.gif") + self.path("*.jpg") + self.path("*.css") + self.end_prefix("html/*") + self.end_prefix("skins") + + self.path("featuretable.txt") + self.path("releasenotes.txt") + self.path("lsl_guide.html") + self.path("gpu_table.txt") + + def flags_list(self): + """ Convenience function that returns the command-line flags for the grid""" + if(self.args['grid'] == ''): + return "" + elif(self.args['grid'] == 'firstlook'): + return '-settings settings_firstlook.xml' + else: + return ("-settings settings_beta.xml --%(grid)s -helperuri http://preview-%(grid)s.secondlife.com/helpers/" % {'grid':self.args['grid']}) + + def login_url(self): + """ Convenience function that returns the appropriate login url for the grid""" + if(self.args.get('login_url')): + return self.args['login_url'] + else: + if(self.args['grid'] == ''): + return 'http://secondlife.com/app/login/' + elif(self.args['grid'] == 'firstlook'): + return 'http://secondlife.com/app/login/firstlook/' + else: + return 'http://secondlife.com/app/login/beta/' + + def replace_login_url(self): + # set the login page to point to a url appropriate for the type of client + self.replace_in("skins/xui/en-us/panel_login.xml", searchdict={'http://secondlife.com/app/login/':self.login_url()}) + + def create_unpacked(self): + unpacked_file_name = "unpacked_%(plat)s_%(vers)s.tar" % { + 'plat':self.args['platform'], + 'vers':'_'.join(self.args['version'])} + print "Creating unpacked file:", unpacked_file_name + # could add a gz here but that doubles the time it takes to do this step + tf = tarfile.open(self.src_path_of(unpacked_file_name), 'w:') + # add the entire installation package, at the very top level + tf.add(self.get_dst_prefix(), "") + tf.close() + +class WindowsManifest(ViewerManifest): + def final_exe(self): + # *NOTE: these are the only two executable names that the crash reporter recognizes + if self.args['grid'] == '': + return "SecondLife.exe" + else: + return "SecondLifePreview.exe" + # return "SecondLifePreview%s.exe" % (self.args['grid'], ) + + def construct(self): + super(WindowsManifest, self).construct() + # the final exe is complicated because we're not sure where it's coming from, + # nor do we have a fixed name for the executable + self.path(self.find_existing_file('ReleaseForDownload/Secondlife.exe', 'Secondlife.exe', 'ReleaseNoOpt/newview_noopt.exe'), dst=self.final_exe()) + # need to get the kdu dll from any of the build directories as well + self.path(self.find_existing_file('ReleaseForDownload/llkdu.dll', 'llkdu.dll', '../../libraries/i686-win32/lib_release/llkdu.dll'), dst='llkdu.dll') + self.path(src="licenses-win32.txt", dst="licenses.txt") + + # For use in crash reporting (generates minidumps) + self.path("dbghelp.dll") + + # For using FMOD for sound... DJS + self.path("fmod.dll") + + # Mozilla appears to force a dependency on these files so we need to ship it (CP) + self.path("msvcr71.dll") + self.path("msvcp71.dll") + + # Mozilla runtime DLLs (CP) + if self.prefix(src="../../libraries/i686-win32/lib_release", dst=""): + self.path("gksvggdiplus.dll") + self.path("js3250.dll") + self.path("nspr4.dll") + self.path("nss3.dll") + self.path("nssckbi.dll") + self.path("plc4.dll") + self.path("plds4.dll") + self.path("smime3.dll") + self.path("softokn3.dll") + self.path("ssl3.dll") + self.path("xpcom.dll") + self.path("xul.dll") + self.end_prefix() + + # Mozilla runtime misc files (CP) + if self.prefix(src="app_settings/mozilla"): + self.path("chrome/*.*") + self.path("components/*.*") + self.path("greprefs/*.*") + self.path("plugins/*.*") + self.path("res/*.*") + self.path("res/*/*") + self.end_prefix() + + # pull in the crash logger and updater from other projects + self.path(src="../win_crash_logger/win_crash_logger.exe", dst="win_crash_logger.exe") + self.path(src="../win_updater/updater.exe", dst="updater.exe") + self.replace_login_url() + + def nsi_file_commands(self, install=True): + def wpath(path): + if(path.endswith('/') or path.endswith(os.path.sep)): + path = path[:-1] + path = path.replace('/', '\\') + return path + + result = "" + dest_files = [pair[1] for pair in self.file_list if pair[0] and os.path.isfile(pair[1])] + # sort deepest hierarchy first + dest_files.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b)) + dest_files.reverse() + out_path = None + for pkg_file in dest_files: + rel_file = os.path.normpath(pkg_file.replace(self.get_dst_prefix()+os.path.sep,'')) + installed_dir = wpath(os.path.join('$INSTDIR', os.path.dirname(rel_file))) + pkg_file = wpath(os.path.normpath(pkg_file)) + if installed_dir != out_path: + if(install): + out_path = installed_dir + result += 'SetOutPath ' + out_path + '\n' + if(install): + result += 'File ' + pkg_file + '\n' + else: + result += 'Delete ' + wpath(os.path.join('$INSTDIR', rel_file)) + '\n' + # at the end of a delete, just rmdir all the directories + if(not install): + deleted_file_dirs = [os.path.dirname(pair[1].replace(self.get_dst_prefix()+os.path.sep,'')) for pair in self.file_list] + # find all ancestors so that we don't skip any dirs that happened to have no non-dir children + deleted_dirs = [] + for d in deleted_file_dirs: + deleted_dirs.extend(path_ancestors(d)) + # sort deepest hierarchy first + deleted_dirs.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b)) + deleted_dirs.reverse() + prev = None + for d in deleted_dirs: + if d != prev: # skip duplicates + result += 'RMDir ' + wpath(os.path.join('$INSTDIR', os.path.normpath(d))) + '\n' + prev = d + + return result + + def package_finish(self): + self.create_unpacked() + version_vars_template = """ + !define INSTEXE "%(final_exe)s" + !define VERSION "%(version_short)s" + !define VERSION_LONG "%(version)s" + !define VERSION_DASHES "%(version_dashes)s" + """ + if(self.args['grid'] == ''): + installer_file = "Second Life %(version_dashes)s Setup.exe" + grid_vars_template = """ + OutFile "%(outfile)s" + !define INSTFLAGS "%(flags)s" + !define INSTNAME "SecondLife" + !define SHORTCUT "Second Life" + !define URLNAME "secondlife" + Caption "Second Life ${VERSION}" + """ + else: + installer_file = "Second Life %(version_dashes)s (%(grid_caps)s) Setup.exe" + grid_vars_template = """ + OutFile "%(outfile)s" + !define INSTFLAGS "%(flags)s" + !define INSTNAME "SecondLife%(grid_caps)s" + !define SHORTCUT "Second Life (%(grid_caps)s)" + !define URLNAME "secondlife%(grid)s" + !define UNINSTALL_SETTINGS 1 + Caption "Second Life %(grid)s ${VERSION}" + """ + if(self.args.has_key('installer_name')): + installer_file = self.args['installer_name'] + else: + installer_file = installer_file % {'version_dashes' : '-'.join(self.args['version']), + 'grid_caps' : self.args['grid'].upper()} + tempfile = "../secondlife_setup.nsi" + # the following is an odd sort of double-string replacement + self.replace_in("installers/windows/installer_template.nsi", tempfile, { + "%%VERSION%%":version_vars_template%{'version_short' : '.'.join(self.args['version'][:-1]), + 'version' : '.'.join(self.args['version']), + 'version_dashes' : '-'.join(self.args['version']), + 'final_exe' : self.final_exe()}, + "%%GRID_VARS%%":grid_vars_template%{'grid':self.args['grid'], + 'grid_caps':self.args['grid'].upper(), + 'outfile':installer_file, + 'flags':self.flags_list()}, + "%%INSTALL_FILES%%":self.nsi_file_commands(True), + "%%DELETE_FILES%%":self.nsi_file_commands(False)}) + + NSIS_path = 'C:\\Program Files\\NSIS\\makensis.exe' + self.run_command('"' + proper_windows_path(NSIS_path) + '" ' + self.dst_path_of(tempfile)) +# self.remove(self.dst_path_of(tempfile)) + self.created_path(installer_file) + + +class DarwinManifest(ViewerManifest): + def construct(self): + # copy over the build result (this is a no-op if run within the xcode script) + self.path("build/" + self.args['configuration'] + "/Second Life.app", dst="") + + if self.prefix(src="", dst="Contents"): # everything goes in Contents + # Expand the tar file containing the assorted mozilla bits into + # /Contents/MacOS/ + self.contents_of_tar('mozilla-universal-darwin.tgz', 'MacOS') +# self.run_command('tar -zx -C "%(macos)s" -f "%(tarfile)s"' % +# {'macos':self.ensure_dst_dir("MacOS"), +# 'tarfile':self.src_path_of("mozilla-universal-darwin.tgz")}) + + # replace the default theme with our custom theme (so scrollbars work). + if self.prefix(src="mozilla-theme", dst="MacOS/chrome"): + self.path("classic.jar") + self.path("classic.manifest") + self.end_prefix("MacOS/chrome") + + # most everything goes in the Resources directory + if self.prefix(src="", dst="Resources"): + super(DarwinManifest, self).construct() + + # the trial directory seems to be not used [rdw] + self.path('trial') + + if self.prefix("cursors_mac"): + self.path("*.tif") + self.end_prefix("cursors_mac") + + self.path("licenses-mac.txt", dst="licenses.txt") + self.path("featuretable_mac.txt") + self.path("secondlife.icns") + + # llkdu dynamic library + self.path("../../libraries/universal-darwin/lib_release/libllkdu.dylib", "libllkdu.dylib") + + # command line arguments for connecting to the proper grid + self.put_in_file(self.flags_list(), 'arguments.txt') + + # set the proper login url + self.replace_login_url() + + self.end_prefix("Resources") + + self.end_prefix("Contents") + + + def package_finish(self): + # NOTE: the -S argument to strip causes it to keep enough info for + # annotated backtraces (i.e. function names in the crash log). 'strip' with no + # arguments yields a slightly smaller binary but makes crash logs mostly useless. + # This may be desirable for the final release. Or not. + self.run_command('strip -S "%(viewer_binary)s"' % + { 'viewer_binary' : self.dst_path_of('Contents/MacOS/Second Life')}) + + self.create_unpacked() + + imagename="SecondLife_" + '_'.join(self.args['version']) + if(self.args['grid'] != ''): + imagename = imagename + '_' + self.args['grid'] + + sparsename = imagename + ".sparseimage" + finalname = imagename + ".dmg" + # make sure we don't have stale files laying about + self.remove(sparsename, finalname) + + self.run_command('hdiutil create "%(sparse)s" -volname "Second Life" -fs HFS+ -type SPARSE -megabytes 200' % {'sparse':sparsename}) + + # mount the image and get the name of the mount point and device node + hdi_output = self.run_command('hdiutil attach -private "' + sparsename + '"') + devfile = re.search("/dev/disk([0-9]+)[^s]", hdi_output).group(0).strip() + volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip() + + # Copy everything in to the mounted .dmg + for s,d in {self.get_dst_prefix():("Second Life " + self.args['grid']).strip()+ ".app", + "lsl_guide.html":"Linden Scripting Language Guide.html", + "releasenotes.txt":"Release Notes.txt", + "installers/darwin/mac_image_hidden":".hidden", + "installers/darwin/mac_image_background.tga":"background.tga", + "installers/darwin/mac_image_DS_Store":".DS_Store"}.items(): + print "Copying to dmg", s, d + self.copy_action(self.src_path_of(s), os.path.join(volpath, d)) + + # Unmount the image + self.run_command('hdiutil detach "' + devfile + '"') + + print "Converting temp disk image to final disk image" + self.run_command('hdiutil convert "%(sparse)s" -format UDZO -imagekey zlib-level=9 -o "%(final)s"' % {'sparse':sparsename, 'final':finalname}) + # get rid of the temp file + self.remove(sparsename) + +class LinuxManifest(ViewerManifest): + def construct(self): + super(LinuxManifest, self).construct() + self.path("licenses-linux.txt","licenses.txt") + self.path("res/ll_icon.ico","secondlife.ico") + if self.prefix("linux_tools", ""): + self.path("client-readme.txt","README-linux.txt") + self.path("wrapper.sh","secondlife") + self.path("unicode.ttf","unicode.ttf") + self.end_prefix("linux_tools") + + # Create an appropriate gridargs.dat for this package, denoting required grid. + self.put_in_file(self.flags_list(), 'gridargs.dat') + # set proper login url + self.replace_login_url() + + def package_finish(self): + if(self.args.has_key('installer_name')): + installer_name = self.args['installer_name'] + else: + installer_name = '_'.join('SecondLife_', self.args.get('arch'), *self.args['version']) + if grid != '': + installer_name += "_" + grid.upper() + + # stripping all the libs removes a few megabytes from the end-user package + for s,d in self.file_list: + if re.search("lib/lib.+\.so.*", d): + self.run_command('strip -S %s' % d) + self.create_unpacked() + # temporarily move directory tree so that it has the right name in the tarfile + self.run_command("mv %(dst)s %(inst)s" % {'dst':self.get_dst_prefix(),'inst':self.src_path_of(installer_name)}) + # --numeric-owner hides the username of the builder for security etc. + self.run_command('tar -C %(dir)s --numeric-owner -cjf %(inst_path)s.tar.bz2 %(inst_name)s' % {'dir':self.get_src_prefix(), 'inst_name': installer_name, 'inst_path':self.src_path_of(installer_name)}) + self.run_command("mv %(inst)s %(dst)s" % {'dst':self.get_dst_prefix(),'inst':self.src_path_of(installer_name)}) + +class Linux_i686Manifest(LinuxManifest): + def construct(self): + super(Linux_i686Manifest, self).construct() + self.path("secondlife-i686-bin-stripped","bin/do-not-directly-run-secondlife-bin") + self.path("../linux_crash_logger/linux-crash-logger-i686-bin-stripped","linux-crash-logger.bin") + self.path("linux_tools/launch_url.sh","launch_url.sh") + if self.prefix("res-sdl"): + self.path("*") + # recurse + self.end_prefix("res-sdl") + + self.path("app_settings/mozilla-runtime-linux-i686", "app_settings/mozilla") + + if self.prefix("../../libraries/i686-linux/lib_release_client", "lib"): + self.path("libkdu_v42R.so") + self.path("libfmod-3.75.so") + self.path("libapr-1.so.0") + self.path("libaprutil-1.so.0") + self.path("libdb-4.2.so") + self.path("libogg.so.0") + self.path("libvorbis.so.0") + self.path("libvorbisfile.so.0") + self.path("libvorbisenc.so.0") + self.path("libcurl.so.3") + self.path("libcrypto.so.0.9.7") + self.path("libssl.so.0.9.7") + self.path("libexpat.so.1") + self.path("libstdc++.so.6") + self.path("libelfio.so") + self.path("libuuid.so", "libuuid.so.1") + self.path("libSDL-1.2.so.0") + self.path("libllkdu.so", "../bin/libllkdu.so") # llkdu goes in bin for some reason + self.end_prefix("lib") + + +class Linux_x86_64Manifest(LinuxManifest): + def construct(self): + super(Linux_x86_64Manifest, self).construct() + self.path("secondlife-x86_64-bin-stripped","bin/secondlife-bin") + # TODO: I get the sense that this isn't fully fleshed out + if self.prefix("../../libraries/x86_64-linux/lib_release_client", "lib"): + self.path("libkdu_v42R.so") + self.path("libxmlrpc.so.0") + # self.path("libllkdu.so", "../bin/libllkdu.so") # llkdu goes in bin for some reason + self.end_prefix("lib") + + +if __name__ == "__main__": + main(srctree=viewer_dir, dsttree=os.path.join(viewer_dir, "packaged")) diff --git a/indra/test/test_llmanifest.py b/indra/test/test_llmanifest.py new file mode 100644 index 0000000000..cc464237a2 --- /dev/null +++ b/indra/test/test_llmanifest.py @@ -0,0 +1,109 @@ +#!/usr/bin/python +# @file test_llmanifest.py +# @author Ryan Williams +# @brief Test cases for LLManifest library. +# +# Copyright (c) 2006-$CurrentYear$, Linden Research, Inc. +# $License$ + +from indra import llmanifest +import os.path +import os +import unittest + +class DemoManifest(llmanifest.LLManifest): + def construct(self): + super(DemoManifest, self).construct() + if self.prefix("dir_1"): + self.path("test_a") + self.path(src="test_b", dst="test_dst_b") + self.path("*.test") + self.path("*.tex", "*.jpg") + if self.prefix("nested", dst=""): + self.path("deep") + self.end_prefix() + self.end_prefix("dir_1") + + +class Demo_ArchManifest(llmanifest.LLManifest): + pass + +class TestLLManifest(unittest.TestCase): + mode='static' + def setUp(self): + self.m = llmanifest.LLManifest("src", "dst", {'grid':'default', 'platform':'darwin', 'version':(1,2,3,4)}) + + def testproperwindowspath(self): + self.assertEqual(llmanifest.proper_windows_path("C:\Program Files", "cygwin"),"/cygdrive/c/Program Files") + self.assertEqual(llmanifest.proper_windows_path("C:\Program Files", "windows"), "C:\Program Files") + self.assertEqual(llmanifest.proper_windows_path("/cygdrive/c/Program Files/NSIS", "windows"), "C:\Program Files\NSIS") + self.assertEqual(llmanifest.proper_windows_path("/cygdrive/c/Program Files/NSIS", "cygwin"), "/cygdrive/c/Program Files/NSIS") + + def testpathancestors(self): + self.assertEqual(["dir"], [p for p in llmanifest.path_ancestors("dir")]) + self.assertEqual(["dir/sub", "dir"], [p for p in llmanifest.path_ancestors("dir/sub")]) + self.assertEqual(["dir/sub", "dir"], [p for p in llmanifest.path_ancestors("dir/sub/")]) + self.assertEqual(["dir/sub/two", "dir/sub", "dir"], [p for p in llmanifest.path_ancestors("dir/sub/two")]) + + + def testforplatform(self): + self.assertEqual(llmanifest.LLManifest.for_platform('demo'), DemoManifest) + def tmp_test(): + return llmanifest.LLManifest.for_platform('extant') + self.assertRaises(KeyError, tmp_test) + ExtantManifest = llmanifest.LLManifestRegistry('ExtantManifest', (llmanifest.LLManifest,), {}) + self.assertEqual(llmanifest.LLManifest.for_platform('extant'), ExtantManifest) + self.assertEqual(llmanifest.LLManifest.for_platform('demo', 'Arch'), Demo_ArchManifest) + + + def testprefix(self): + self.assertEqual(self.m.get_src_prefix(), "src") + self.assertEqual(self.m.get_dst_prefix(), "dst") + self.m.prefix("level1") + self.assertEqual(self.m.get_src_prefix(), "src/level1") + self.assertEqual(self.m.get_dst_prefix(), "dst/level1") + self.m.end_prefix() + self.m.prefix(src="src", dst="dst") + self.assertEqual(self.m.get_src_prefix(), "src/src") + self.assertEqual(self.m.get_dst_prefix(), "dst/dst") + self.m.end_prefix() + + def testendprefix(self): + self.assertEqual(self.m.get_src_prefix(), "src") + self.assertEqual(self.m.get_dst_prefix(), "dst") + self.m.prefix("levela") + self.m.end_prefix() + self.assertEqual(self.m.get_src_prefix(), "src") + self.assertEqual(self.m.get_dst_prefix(), "dst") + self.m.prefix("level1") + self.m.end_prefix("level1") + self.assertEqual(self.m.get_src_prefix(), "src") + self.assertEqual(self.m.get_dst_prefix(), "dst") + self.m.prefix("level1") + def tmp_test(): + self.m.end_prefix("mismatch") + self.assertRaises(ValueError, tmp_test) + + def testruncommand(self): + self.assertEqual("Hello\n", self.m.run_command("echo Hello")) + def tmp_test(): + self.m.run_command("fff_garbage") + self.assertRaises(RuntimeError, tmp_test) + + def testpathof(self): + self.assertEqual(self.m.src_path_of("a"), "src/a") + self.assertEqual(self.m.dst_path_of("a"), "dst/a") + self.m.prefix("tmp") + self.assertEqual(self.m.src_path_of("b/c"), "src/tmp/b/c") + self.assertEqual(self.m.dst_path_of("b/c"), "dst/tmp/b/c") + + def testcmakedirs(self): + self.m.cmakedirs("test_dir_DELETE/nested/dir") + self.assert_(os.path.exists("test_dir_DELETE/nested/dir")) + self.assert_(os.path.isdir("test_dir_DELETE")) + self.assert_(os.path.isdir("test_dir_DELETE/nested")) + self.assert_(os.path.isdir("test_dir_DELETE/nested/dir")) + os.removedirs("test_dir_DELETE/nested/dir") + +if __name__ == '__main__': + unittest.main() -- cgit v1.2.3