Commits / ec3f3bb
Default avatar Ryan Williams committed (raw commit)

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.

Comments (0)

Files changed (3)

indra/newview/installers/windows/installer_template.nsi

+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; 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\<user>\SecondLife\cache
+; Delete files in Documents and Settings\All Users\SecondLife\cache
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+Function RemoveCacheFiles
+
+; Delete files in Documents and Settings\<user>\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\<user>\SecondLife
+; Delete files in Documents and Settings\All Users\SecondLife
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+Function un.DocumentsAndSettingsFolder
+
+; Delete files in Documents and Settings\<user>\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

indra/newview/viewer_manifest.py

+#!/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
+                        #  <bundle>/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"))

indra/test/test_llmanifest.py

+#!/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()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.