Friday, October 8, 2010

Teaching Pigs to Sing (and scripts to make pretty logs)

Today is Friday, and OMFG the weather is increditastical here in sunny Virginia Beach, Virginia!  Going outside feels like having someone rub your scalp and sing pretty songs in your ear.  Yeah, I can’t concentrate on anything else, but… I must.

I had a walk-up request to package MapInfo Pro 10.0 today.  But not just by itself (because that would’ve been too easy, and you can’t have “easy” on a Friday), it had to include another application called “ADAM” and then copy down a whole folder tree of content files and make a shortcut to one of them on the “All Users” Start Menu as well.  It turns out the whole bundle is aimed at installing the “ADAM” software bundle for MapInfo by Deccan International.

The vendor (Pitney-Bowes, and don’t even ask why the **** Pitney-Bowes bought MapInfo.  It makes as much sense as eBay buying Skype, but oh well, onward…) provides a setup.exe, a setup.msi, and a data1.cab file.  They also provide a nice installation guide with tips on running the setup.exe unattended by feeding in arguments using the ancient InstallShield method (you know: setup.exe /v”a bunch of crap here”), but the problem with those is that they’re ill-behaved when strung together in a sequence because they often don’t wait to finish before releasing their “mutex” (go look that one up.  It’s ok.  I’ll wait…).  Are you back?  Ok, so the better option, in most cases, is using the .MSI file and making a transform (.MST) to stuff in the arguments more efficiently.

So, I fired up Wise Package Studio, consumed a coffee, ate a trail mix bar, and made a transform for the installation that inputs the “USERNAME”, “COMPANYNAME”, “PIDKEY” (serial number) and “ACCD” (activation code).  No problem.

You can use Orca instead of Wise Package Studio, or InstallShield, if you prefer.  As long as you can make and edit the .MST it doesn’t matter.

Time to setup the deployment folder structure.  I placed it on a server share named “Packages”, under a root folder named “Deccan”, then copied the MapInfo Pro 10.0 “Disk 1” files into a sub-folder called “MI_PRO\Disk1”.  Then I copied the ADAM installation files under a sub-folder under “Deccan” named “ADAM” and another folder created for “CADAnalyst” under “Deccan”.  The structure looks like this…

\Server\Packages\Deccan\…
-MI_PRO\Disk1
-ADAM
-CADAnalyst

I placed the .MST file into the “Disk1” sub-folder, next to the .MSI file it relates to.

I then created two script files:  setup.bat, and makeshortcut.vbs

Setup.bat simply runs the .MSI with the .MST transform in “quiet” mode.  Then it runs the ADAM .MSI installation, and downloads the data files and makes a shortcut using a call to the makeshortcut.vbs (vbscript) file.  But I also wanted this to generate a self-describing log file so I could backtrace any problems or check on progress, etc.  The “echo” command is simple enough, and I add %DATE% and %TIME% to make a standard “timestamped” log entry output.

However, VBScript uses a completely different format for showing a full date, so I had to beat that into shape with a hammer (see the makeshortcut.vbs script at the end).  I needed the output from both to merge into one log file and look consistent.  That’s just me.  Maybe you don’t care that much, which is fine.  I care about a lot of stupid things.

@echo off
01. rem ****************************************************************
02. rem Filename..: setup.bat
03. rem Author....: Guess Who? (skatterbrainz.blogspot.com)
04. rem Date......: 10/08/2010
05. rem Purpose...: install MapInfo 10 + ADAM + CAD Analyst files
06. rem ****************************************************************
07. CLS
08. SETLOCAL
09. SET LOG=%TMP%\mapinfo10_setup.log
10. echo %DATE% %TIME% initializing script [test_setup] >%LOG%
11. echo %DATE% %TIME% source = "%~dps0" >>%LOG%
12. echo %DATE% %TIME% target = %computername% >>%LOG%
13. echo %DATE% %TIME% tmp = %tmp% >>%LOG%
14. echo ---------------------------------------------- >>%LOG%
15. echo %DATE% %TIME% installing MapInfo Pro 10... >>%LOG%
16. msiexec /i "%~dps0MI_PRO\Disk1\MapInfo Professional 10.0.msi" TRANSFORMS="%~dps0MI_PRO\Disk1\MapInfoPro10_1.mst" /quiet /norestart
17. echo %DATE% %TIME% completed / result code = %errorlevel% >>%LOG%
18. rem ----------------------------------------------
19. if exist "c:\Program Files\Deccan International\ADAM.mbx" (
20. echo %DATE% %TIME% ADAM has already been installed >>%LOG%
21. ) else (
22. echo %DATE% %TIME% installing HPC ADAM ... >>%LOG%
23. msiexec /i "%~dps0ADAM\ADAMsetup.msi" /quiet /norestart
24. echo %DATE% %TIME% completed / result code = %errorlevel% >>%LOG%
25. )
26. rem ----------------------------------------------
27. if exist "c:\Program Files\Deccan International" (
28. if exist "c:\Program Files\Deccan International\CADAnalyst" (
29. echo %DATE% %TIME% the CADAnalyst folder was already created >>%LOG%
30. ) else (
31. echo %DATE% %TIME% downloading data files for CAD Analyst... >>%LOG%
32. xcopy "%~dps0CADAnalyst\*.*" "c:\Program Files\Deccan International\CADAnalyst\*.*" /s /y
33. echo %DATE% %TIME% download complete... >>%LOG%
34. )
35. ) else (
36. echo %DATE% %TIME% error / target folder Deccan International not found! >>%LOG%
37. exit /b 1
38. )
39. rem ----------------------------------------------
40. if exist "%allusersprofile%\Start Menu\Programs\CAD Analyst.lnk" (
41. echo %DATE% %TIME% shortcut already exists >>%LOG%
42. ) else (
43. echo %DATE% %TIME% creating a start menu shortcut for cad analyst .mbx file... >>%LOG%
44. cscript /nologo %~dps0makeshortcut.vbs "N=CAD Analyst" "T=c:\Program Files\Deccan International\CADAnalyst\CAD Analyst.MBX" "L=2" "W=c:\Program Files\Deccan International\CADAnalyst" "D=CAD Analyst" >>%LOG%
45. )
46. echo %DATE% %TIME% installation complete! >>%LOG%
47. ENDLOCAL
48. exit /b %errorlevel%


The %errorlevel% variable is used to capture and display the result code of the last-run command, which is “msiexec” in each case.  Then I force a script “exit” using the /b option and a return code that is non-zero if an error occurs.  If all proceeds along fine and finishes properly, the %errorlevel% result code at the very end should be zero (0).  Most deployment tools see zero as “good” or “no errors occurred”.



Then comes the makeshortcut.vbs script, for making the shortcut.  I could have downloaded some utility .exe to call instead, but I prefer making my own stuff so I know who to blame when it explodes or implodes.



'****************************************************************
' Filename..: makeShortcut.vbs
' Author....: Guess Who? (skatterbrainz.blogspot.com)
' Date......: 10/08/2010
' Purpose...: create a shortcut
'****************************************************************
Dim objArgs, fso, oShell, i, arg, scName, scTarget
Dim scFolder, scCaption, scLoc

Echo "info: makeShortcut.vbs --> processing inputs..."
Set objArgs = WScript.Arguments
If objArgs.Count = 0 Then
Echo "error: incorrect number of arguments supplied"
Echo "-- example..."
Echo "-- makeShortcut.vbs ""N=name"" ""T=target"" ""W=folder"" ""D=description"" ""L=loc"""
wscript.Quit(1)
Else
Echo "info: " & objArgs.Count & " arguments were provided"
Set fso = CreateObject("Scripting.FileSystemObject")
Set oShell = CreateObject("Wscript.Shell")
For i = 0 to objArgs.Count - 1
arg = objArgs(i)
Select Case Ucase(Left(arg,2))
Case "N=":
scName = Mid(arg,3)
Case "L=":
Select Case Right(arg,1)
Case "1":
' all-users desktop
scLoc = oShell.ExpandEnvironmentStrings("%allusersprofile%") & "\Desktop"
Case "2":
' all-users start menu / programs
If fso.FolderExists("C:\ProgramData\Microsoft\Windows\Start Menu\Programs") Then
' check for windows 7 client first
scLoc = "C:\ProgramData\Microsoft\Windows\Start Menu\Programs"
Else
' default to assuming windows xp client
scLoc = oShell.ExpandEnvironmentStrings("%allusersprofile%") & "\Start Menu\Programs"
End If
Case "3":
' current user desktop
scLoc = oShell.ExpandEnvironmentStrings("%userprofile%") & "\Desktop"
Case "4":
scLoc = Mid(arg,3)
End Select
Case "T=":
scTarget = Mid(arg,3)
If fso.FileExists(scTarget) Then
Echo "info: shortcut target has been verified"
Else
Echo "error: shortcut target could not be found!"
wscript.Quit(2)
End If
Case "W=":
scFolder = Mid(arg,3)
If fso.FolderExists(scFolder) Then
Echo "info: shortcut working folder verified"
Else
Echo "error: shortcut working folder could not be found"
wscript.Quit(3)
End If
Case "D=":
scCaption = Mid(arg,3)
End Select
Next
If scFolder = "" or scTarget = "" or scName = "" or scLoc = "" Then
Echo "error: insufficient arguments provided"
wscript.Quit(4)
Else
Echo "validating shortcut parameters..."
Echo "-- name = " & scName
Echo "-- target = " & scTarget
Echo "-- path = " & scFolder
Echo "-- location = " & scLoc
Echo "-- caption = " & scCaption
On Error Resume Next
Set oShortcut = oShell.CreateShortcut(scLoc & "\" & scName & ".lnk")
If err.Number = 0 Then
Echo "info: creating shortcut..."
oShortcut.TargetPath = scTarget
oShortcut.WorkingDirectory = scFolder
oShortcut.IconLocation = scTarget & ",0"
oShortcut.WindowStyle = 1
oShortcut.Description = scCaption
oShortcut.Save
If fso.FileExists(scLoc & "\" & scName & ".lnk") Then
Echo "-- shortcut has been created"
Else
Echo "-- failed to create shortcut!"
End If
Else
Echo "-- access denied to the specified location!"
End If
End If
End If

'----------------------------------------------------------------
' comment: echo runtime progress
'----------------------------------------------------------------

Sub Echo(s)
wscript.Echo DosDate() & " " & s
End Sub

'----------------------------------------------------------------
' comment: format date stamp to match DOS %DATE% %TIME% format
'----------------------------------------------------------------

Function DosDate()
dn = Left(WeekDayName(WeekDay(Now)),3)
mo = PadLeft(Month(Now), "0", 2)
dd = PadLeft(Day(Now), "0", 2)
yr = Year(Now)
x = mo & "/" & dd & "/" & yr
DosDate = dn & " " & x & " " & FormatDateTime(Now, vbShortTime) & ".00.00"
End Function

'----------------------------------------------------------------
' comment: pad string on left side with char to requested length
'----------------------------------------------------------------

Function PadLeft(sVal, ch, max)
Dim tmp
tmp = Trim(sVal)
Do Until Len(tmp) = max
tmp = ch & tmp
Loop
PadLeft = tmp
End Function


Together, these run the installations, copy the files, make the shortcut, create a nice log, save the girl, kill the bad guy, and free the hostages, with rolling credits at the end and sweet theme music.



Now I can check the “mapinfo10_setup.log” under the %TEMP% folder path to see what’s going on.  Note that when you deploy this as a SCCM package, it runs as the local account “NT_AUTHORITY\SYSTEM” (aka “SYSTEM”), so the %TMP% and %TEMP% paths are C:\WINDOWS\TEMP (or %WINDIR%\TEMP).



I’m done.  Beer time folks.  It’s a tough job, but someone’s gotta do it.

No comments: