1# $Id: s_winmsi.fcn,v 1.15 2008/04/17 01:59:10 alexg Exp $ 2# 3# The common functions used by the s_winmsi scripts (both 4# for core DB and DB/XML). 5# 6# This script uses several bash extensions that are convenient 7# since we "know" it will always run under Cygwin: shell functions, 8# 'return', declaration of 'local' variables, $(command) syntax, 9# ${#X} (counting chars), ${X#regexp} (searching) $((expr)) (arithmetic) 10# 11# These functions use 'global' variables: 12# ERRORLOG - a filename 13# PRODUCT_NAME - e.g. "Berkeley DB" 14# PRODUCT_VERSION - e.g. "4.1.25", derived from dist/RELEASE 15# PRODUCT_MAJOR - e.g. "4", (for release 4.1.25) from dist/RELEASE 16# PRODUCT_MINOR - e.g. "1", (for release 4.1.25) from dist/RELEASE 17# PRODUCT_PATCH - e.g. "25", (for release 4.1.25) from dist/RELEASE 18# PRODUCT_MAJMIN - e.g. "41", (for release 4.1.25) from dist/RELEASE 19# PRODUCT_STAGE - the staging directory for temp files and builds 20# PRODUCT_LICENSEDIR - the tree containing LICENSE and README 21# PRODUCT_SUB_BLDDIR - top of the subproduct build e.g. "dbxml-2.0.1/dbxml" 22# PRODUCT_BLDDIR - top of the build tree e.g. "dbxml-2.0.1" 23# PRODUCT_SRCDIR - the dir we unzip to e.g. "dbxml-2.0.1" 24# PRODUCT_DBBUILDDIR - where build_unix dir is for Berkeley DB (for Perl) 25# PRODUCT_SHARED_WINMSIDIR - where the master winmsi directory is 26# PRODUCT_IMAGEDIR - where the images are (usually winmsi/images) 27# PRODUCT_ZIP_FILEFMT - what zip file looks like e.g. "db-X.Y.Z.NC.zip" 28# PRODUCT_MSI_FILEFMT - what msi file looks like e.g. "db-X.Y.Z.NC.msi" 29# 30# Some of these may seem redundant, but there are options to take 31# an already built tree from a different place than where we'll unzip 32# to and take our sources from, for example. This allows a lot of flexibility 33# for development and debugging (especially when these trees can be huge). 34 35# This is the magic tag that creates a new unique GUID in Wix. 36# GUIDs are needed on every <Component ... > entry to ensure 37# that the component can be uninstalled. 38GENGUID='Guid="GUID_CREATE_UNIQUE()"' 39PERSISTGUID='Guid="WIX_DB_PERSISTENT_GUID()"' 40 41# MakeRtf() 42# Standard input is plain text, standard output is RTF. 43# 44MakeRtf() { 45 temp1=/tmp/sbm$$a 46 cat > $temp1 47 48 49# Courier is a good font, but the lines with all caps 50# overflows our current dialog size: 51# {\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Courier New;}} 52# \viewkind4\uc1\pard\lang1033\f0\fs16 53# 54# Using Small fonts works: 55# {\rtf1\ansi\deff0{\fonttbl{\f0\fswiss\fprq2\fcharset0 Small Fonts;}} 56# {\colortbl ;\red0\green0\blue0;} 57# \viewkind4\uc1\pard\cf1\lang1033\f0\fs14 58 59# Arial is the best compromise: 60 sed -e 's/^ *//' << 'EndOfRTFHeader' 61 {\rtf1\ansi\deff0{\fonttbl{\f0\fswiss\fprq2\fcharset0 Arial;}} 62 {\colortbl ;\red0\green0\blue0;} 63 \viewkind4\uc1\pard\cf1\lang1033\f0\fs16 64EndOfRTFHeader 65 66# Embedded '<' and '>' can cause problems for Wix 67 sed -e 's:$:\\par:' -e 's:<: \\lquote :' -e 's:>: \\rquote :' < $temp1 68 echo -n '}' 69 rm -f $temp1 70} 71 72# NextId() 73# Get the next available unique id, a simple integer counter. 74# We use a file, rather than a shell variable to track the 75# number, because this is called from subshells at various 76# points, and they cannot affect the variables in the parent shell. 77# 78ComponentID=component.id 79NextId() 80{ 81 local id=`cat $ComponentID 2>/dev/null` 82 if [ "$id" = '' ]; then 83 id=0 84 fi 85 id=$(($id + 1)) 86 echo "$id" > $ComponentID 87 echo "$id" 88} 89 90# CleanFileName(FILENAME) 91# Removes any strange characters in file names, 92# returning the new name on standard output. 93CleanFileName() 94{ 95 echo "$1" | sed -e 's/[-%@!]//g' 96} 97 98# GetShortName(FILENAME) 99# Get a Windows short name for the file, 100# to fit into the 8.3 name space. 101# This is not a great algorithm, but it works. 102# The fact is, the names must be unique, but on 103# Win2000 and WinXP, we'll never see them. 104 105ShortID=short.id 106GetShortName() 107{ 108 local name=`echo "$1" | tr '[a-z]' '[A-Z]'` 109 110 # See if the name fits into 8.3. If so, 111 # return it right away. 112 # 113 case "$name" in 114 ?????????*.* ) ;; 115 *.????* ) ;; 116 *.*.* ) ;; 117 *[-%@!]* ) ;; 118 *.* ) echo "$name" 119 return 120 ;; 121 * ) 122 if [ "${#1}" -le 8 ]; then 123 echo "$name" 124 return 125 fi 126 ;; 127 esac 128 129 # From NAMEISLONG.EXTLONG, build a name 130 # like NAME~ZZZ.EXT, where ZZZ is a unique (hex) 131 # number we build. This is 132 133 local id=`cat $ShortID 2>/dev/null` 134 if [ "$id" = '' ]; then 135 id=0 136 fi 137 id=$(($id + 1)) 138 echo "$id" > $ShortID 139 if [ "$id" -ge 4096 ]; then 140 echo "BADBADBAD.TXT" # return something that will give an error 141 Error "ShortId overflow" 142 exit 1 143 fi 144 145 # Convert the id to hex (I ran out of space using decimal) 146 # This is too slow: id=`echo 16 o $id p | dc` 147 id=`printf "%x" $id` 148 149 # Collect and clean up the part of the name before, and after, the dot 150 local before=`CleanFileName "$name" | sed -e 's/^\([^.]*\)[.].*$/\1/'` 151 local after=`CleanFileName "$name" | sed -e 's/^[^.]*[.]\(.*\)$/\1/'` 152 153 # Make sure the before part fits in 5 chars (not 8, since 154 # we need a few for the unique number). 155 if [ "${#before}" -gt 5 ]; then 156 before=`echo "$before" | sed -e 's/^\(.....\).*/\1/'` 157 fi 158 if [ "${#after}" -gt 3 ]; then 159 after=`echo "$after" | sed -e 's/^\(...\).*/\1/'` 160 fi 161 echo "${before}~${id}.${after}" 162} 163 164# Progress([OPTION,]STRING...) 165# Show a major processing step via echo to stdout and to the error log. 166# An OPTION is "-minor", indicating no big banner. 167# 168Progress() 169{ 170 if [ "$1" = -minor ]; then 171 shift 172 else 173 echo "" >> $ERRORLOG 174 echo "============================" >> $ERRORLOG 175 fi 176 echo "$@" >> $ERRORLOG 177 echo "$@" >&15 178} 179 180# Error(STRING...) 181# Show an error in a standard way. 182# 183Error() 184{ 185 echo "" >> $ERRORLOG 186 echo "****************** FAIL ******************" >> $ERRORLOG 187 echo "ERROR: $@" >> $ERRORLOG 188 echo "ERROR: $@" >&15 189 echo "See $ERRORLOG for details" >&15 190 return 1 191} 192 193# RequireFileInPath(NAME, PATHVAL, FILE) 194# Look for FILE in the path that has value PATHVAL. 195# The path's name is NAME if it needs to be shown. 196# 197RequireFileInPath() 198{ 199 local type="$1" 200 local origpath="$2" 201 local file="$3" 202 local upath="$origpath" 203 if [ "$1" != PATH ]; then 204 upath=`cygpath -up "$origpath"` 205 fi 206 207 SAVEIFS="$IFS" 208 IFS=":" 209 found=no 210 for dir in $upath; do 211 if [ -f "$dir/$file" ]; then 212 IFS="$SAVEIFS" 213 return 214 fi 215 done 216 IFS="$SAVEIFS" 217 Error "File $file not found in $type path: $origpath" 218 exit 1 219} 220 221# Rand4X() 222# Return 4 random hex digits on output 223# 224Rand4X() { 225 # The sed command pads the front with 0's as needed 226 (echo 'obase=16'; echo $RANDOM ) | bc | 227 sed -e 's/^/0000/' -e 's/^.*\(....\)$/\1/' 228 229} 230 231# RunM4() 232# Run M4, making appropriate substitutions. 233# This function uses GLOBAL variables: PRODUCT_VERSION (e.g. "4.1.25") 234# and PRODUCT_LICENSEDIR, which is where certain text files are found 235# 236RunM4() { 237 238 # Given a version number, like 2.3.45, we want to 239 # create a 8 character name for the directory like db2_3_45. 240 # This name is under a "Oracle" directory, 241 # so it only needs to be unique within the universe of BDB versions. 242 # TODO: instead of using a version number like $DB_VERSION, 243 # maybe use $DB_VERSION_UNIQUE_NAME which looks like "_2003" 244 245 local DB_8CHAR_VERSION=`echo $PRODUCT_VERSION | sed -e 's/[.]/_/g'` 246 if [ ${#DB_8CHAR_VERSION} -le 6 ]; then 247 DB_8CHAR_VERSION="db$DB_8CHAR_VERSION" 248 elif [ ${#DB_8CHAR_VERSION} -le 7 ]; then 249 DB_8CHAR_VERSION="d$DB_8CHAR_VERSION" 250 else 251 Error "Version number too large for simple version number algorithm" 252 exit 1 253 fi 254 255 # Remove leading ./ from PRODUCT_LICENSEDIR if present. 256 local licensedir=`cygpath -w "$PRODUCT_LICENSEDIR"` 257 258 # Create a GUID prefix of the form: ????????-????-????-????-???? 259 # This leaves 8 digits of GUID to be manipulated by m4. 260 local GUID_PREFIX="`Rand4X``Rand4X`-`Rand4X`-`Rand4X`-`Rand4X`-`Rand4X`" 261 262 # -P requires that all m4 macros, like define, eval, etc. 263 # are prefixed, like m4_define, m4_eval, etc. This avoids 264 # various name conflicts with input files. 265 # TODO: rename DB_SRCDIR as DB_LICENSEDIR 266 m4 -P \ 267 -DWIX_DB_VERSION="$PRODUCT_VERSION" \ 268 -DWIX_DB_8CHAR_VERSION="$DB_8CHAR_VERSION" \ 269 -DWIX_DB_GUID_PREFIX="$GUID_PREFIX" \ 270 -DWIX_DB_PRODUCT_NAME="$PRODUCT_NAME" \ 271 -DWIX_DB_SRCDIR="$licensedir" \ 272 -DWIX_DB_TOP="`cygpath -w $PRODUCT_BLDDIR`" \ 273 -DWIX_DB_SHARED_WINMSIDIR="$PRODUCT_SHARED_WINMSIDIR" \ 274 -DWIX_DB_IMAGEDIR="`cygpath -w $PRODUCT_IMAGEDIR`" \ 275 -DWIX_DB_FEATURE_STRUCTURE="m4_include(features.wixinc)" \ 276 -DWIX_DB_DIRECTORY_STRUCTURE="m4_include(directory.wixinc)" \ 277 -DWIX_DB_LINKS="m4_include(links.wixinc)" \ 278 -DWIX_DB_LICENSE_RTF="m4_include(license.rtf)" \ 279 -DWIX_DB_ENV_FEATURE_PROPS="m4_include(envprops.wixinc)" \ 280 -DWIX_DB_ENV_FEATURE_SET="m4_include(envset.wixinc)" \ 281 -DWIX_DB_ENV_FEATURE_SHOW="m4_include(envshow.wixinc)" 282} 283 284# RunTallow(DIR, OPTIONS) 285# Run Tallow, a tool from the WiX distribution 286RunTallow() { 287 local dir="$1" 288 shift 289 290 Id1=`NextId` 291 Id2=`NextId` 292 Id3=`NextId` 293 294 # Tallow is a tool that walks a tree, producing 295 # a WiX directory heirarchy naming the files. 296 # The IDs it produces are not unique (between tallow 297 # runs), so we must make them so here. Thus "directory78" 298 # becomes "MyFeatureName.123.78" where 123 is an id from NextId. 299 # Secondly, instead of using the tallow output as a separately 300 # compiled fragment, we want to include it directly, so 301 # we need to strip out some extraneous XML entries at the top 302 # and bottom of its output. 303 # 304 # Another thing we do is when we see <Directory></Directory> 305 # pairs, we call m4 macros WIX_DB_{BEGIN,END}_SUBDIR because 306 # we need to track the current directory to generate 'persistent' 307 # GUIDs. See the discussion about GUIDs in dbwix.m4 . 308 # 309 # !!! For stripping out the extraneous XML, we rely heavily 310 # !!! on the output format, so this is likely to be fragile 311 # !!! between versions of tallow. Fortunately, it should fail hard. 312 # 313 echo "=============" >> tallow.log 314 echo tallow -nologo -d `cygpath -w "$dir"` "$@" >> tallow.log 315 echo " <!-- TALLOW output begins here -->" 316 tallow -nologo -d `cygpath -w "$dir"` "$@" > tallow.out || exit 1 317 cat tallow.out >> tallow.log 318 echo "-------------" >> tallow.log 319 320 sed -e '1,/<DirectoryRef/d' -e '/<\/DirectoryRef/,$d' \ 321 -e "s/Id=\"directory/Id=\"$feature.$Id1./" \ 322 -e "s/Id=\"component/Id=\"$feature.$Id2./" \ 323 -e "s/Id=\"file/Id=\"$feature.$Id3./" \ 324 -e '/^ <Directory/d' \ 325 -e '/^ <\/Directory/d' \ 326 -e '/<Directory/s/Name=\"\([^"]*\)"/Name="\1" WIX_DB_BEGIN_SUBDIR(\1) /' \ 327 -e '/<\/Directory>/s/$/ WIX_DB_END_SUBDIR()/' \ 328 -e "/<Component/s/>/ $PERSISTGUID>/" \ 329 < tallow.out > tallow.postsed || exit 1 330 331 echo 'WIX_DB_SET_CURFILE()' 332 echo 'WIX_DB_CLEAR_SUBDIR()' 333 cat tallow.postsed 334 echo 'WIX_DB_CLEAR_SUBDIR()' 335 336 cat tallow.postsed >> tallow.log 337 echo " <!-- TALLOW output ends here -->" 338} 339 340# ProcessFeatures(INFILES, INFEATURES, INENV, OUTDIRECTORIES, OUTFEATURES, 341# OUTSET) 342# Use the files.in and features.in files as 343# input to create two output files, one containing a WiX XML 344# fragment showing directories and needed files, 345# and another containing a WiX XML fragment showing 346# the features in a dependency tree. 347# 348# This creates the heart of the installer flexibility. 349# 350ProcessFeatures() { 351 InFiles="infiles.tmp"; CleanInputFile "$1" "$InFiles" 3 4 352 InFeatures="infeatures.tmp"; CleanInputFile "$2" "$InFeatures" 3 4 353 InEnv="inenv.tmp"; CleanInputFile "$3" "$InEnv" 3 4 354 OutDirs="$4" 355 OutFeatures="$5" 356 OutSet="$6" 357 358 rm -f $OutDirs; touch $OutDirs 359 rm -f $OutFeatures; touch $OutFeatures 360 361 # Initialize the feature list. 362 # This will be expanded (per feature) in ProcessOneFeature 363 # 364 XmlLevel=4 365 Xecho "<Publish Property=\"FeatureList\" Value=\"[NULL]\">" >> $OutSet 366 Xecho " <![CDATA[1]]></Publish>" >> $OutSet 367 368 Dirs=`cut -f 3 < $InFiles | sort | uniq` 369 Prevdir="/" 370 ProcessDirTransition "$Prevdir" "/" >> $OutDirs 371 372 for Dir in $Dirs; do 373 ProcessDirTransition "$Prevdir" "$Dir" >> $OutDirs 374 ProcessOneDirectory "$Dir" < $InFiles >> $OutDirs || exit 1 375 Prevdir="$Dir" 376 done 377 ProcessDirTransition "$Prevdir" "/" >> $OutDirs 378 379 cat $InEnv | ( 380 read line 381 while [ "$line" != '' ]; do 382 local FeatureName=`echo "$line" | cut -f 1` 383 local EnvVariable=`echo "$line" | cut -f 2` 384 local EnvValue=`echo "$line" | cut -f 3` 385 local EnvOption=`echo "$line" | cut -f 4` 386 ProcessOneEnv "$FeatureName" "$EnvVariable" "$EnvValue" "$EnvOption" "$OutDirs" "$OutSet" 387 read line 388 done 389 return 0 390 ) || Error "Error processing environment" || exit 1 391 392 cat $InFeatures | ( 393 read line 394 while [ "$line" != '' ]; do 395 local FeaturePath=`echo "$line" | cut -f 1` 396 local ShortName=`echo "$line" | cut -f 2 | StripDoubleQuotes` 397 local Description=`echo "$line" | cut -f 3 | StripDoubleQuotes` 398 local FeatureOptions=`echo "$line" | cut -f 4 | StripDoubleQuotes` 399 ProcessOneFeature "$FeaturePath" "$ShortName" "$Description" "$FeatureOptions" "$OutDirs" "$OutFeatures" "$OutSet" 400 read line 401 done 402 return 0 403 ) || Error "Error processing features" || exit 1 404 405# (PBR) 406# This test code didn't work. My hope was that I could force INSTALLLEVEL 407# to 4 and this would then enable the debug features. 408# Xecho "<Publish Property=\"INSTALLLEVEL\" Value=\"4\" />" >> $OutSet 409# Xecho "<Publish Event=\"SetInstallLevel\" Value=\"4\" />" >> $OutSet 410 411} 412 413# ProcessLinks(INLINKS, OUTFEATURES) 414# Process the INLINKS file, and produce XML on stdout. 415# Each line of the input file requires the creation 416# of a '.URL' file in the installation, and a Shortcut 417# in the Windows menu to point to that. 418# Also add the components generated to a feature, put in OUTFEATURES. 419# 420# TODO: We ought to have a Features column in the links.in file, 421# otherwise, the local doc link is always installed. 422# 423ProcessLinks() { 424 # Set a var to a carriage return without actually putting one in this file 425 local CR=`echo A | tr A '\015'` 426 local InLinks="infiles.tmp"; CleanInputFile "$1" "$InLinks" 3 4 427 local here_win=`cygpath -w $(pwd)` 428 # TODO: maybe get a real modification time, but not sure why we need it. 429 local MODTIMEHEX="0000000007DCC301DE" 430 XmlLevel=6 431 local OutFeatures="$2" 432 433 Xecho + "<Feature Id=\"LinksFeature\" Title=\"Links\"" >> $OutFeatures 434 Xecho " Description=\"Links\" Display=\"hidden\"" >> $OutFeatures 435 Xecho " Level=\"1\" AllowAdvertise=\"no\"" >> $OutFeatures 436 Xecho " ConfigurableDirectory=\"INSTALLUTIL\"" >> $$OUTFeatures 437 Xecho " Absent=\"disallow\">" >> $OutFeatures 438 439 Xecho "<DirectoryRef Id=\"INSTALLUTIL\">" 440 Xecho " <Directory Id=\"INSTALLURL\" Name=\"url\">" 441 Xecho "WIX_DB_SET_CURDIR(/installutil/url)" 442 cat $InLinks | ( 443 read line 444 while [ "$line" != '' ]; do 445 local Shortname=`echo "$line" | cut -f 1 | StripDoubleQuotes` 446 local Name=`echo "$line" | cut -f 2 | StripDoubleQuotes` 447 local Url=`echo "$line" | cut -f 3 | StripDoubleQuotes` 448 read line 449 450 # We register the name .bdbsc extension to get the proper icon 451 local UrlName="$Shortname.bdbsc" 452 local UrlShortName="$Shortname.d1b" 453 local TargetFile="[INSTALLDIR]\\installutil\\url\\$UrlName" 454 local CreateUrlFile=true 455 local CommandShortcut=false 456 local Program="" 457 case "$Url" in 458 file:* ) CreateUrlFile=false 459 TargetFile=`echo $Url | sed -e 's/file://'` 460 TargetFile="[INSTALLDIR]"`cygpath -w $TargetFile`;; 461 cmd:* ) CreateUrlFile=false 462 UrlName="$Shortname.bat" 463 UrlShortName="$Shortname.bat" 464 TargetFile="[INSTALLDIR]\\installutil\\url\\$UrlName" 465 Program=`echo $Url | sed -e 's/cmd://'` 466 CommandShortcut=true;; 467 esac 468 469 Xecho "WIX_DB_SET_CURFILE($Shortname)" 470 Xecho + "<Component Id=\"Links.$Shortname\"" 471 Xecho " $PERSISTGUID" 472 Xecho " SharedDllRefCount=\"yes\" Location=\"either\">" 473 474 if $CreateUrlFile; then 475 echo "[Default]$CR" > $UrlName 476 echo "BASEURL=$Url$CR" | RunM4 >> $UrlName || exit 1 477 echo "[InternetShortcut]$CR" >> $UrlName 478 echo "URL=$Url$CR" | RunM4 >> $UrlName || exit 1 479 echo "Modified=$MODTIMEHEX$CR" >> $UrlName 480 # TODO: we could have an Entry for IconFile=oracleweb.ico IconIndex=1? 481 echo '' 482 Xecho "<File Id=\"File.$Shortname\" " 483 Xecho " LongName=\"$UrlName\" Name=\"$UrlShortName\"" 484 Xecho " Compressed=\"yes\" DiskId=\"1\"" 485 Xecho " src=\"$here_win\\$UrlName\" />" 486 fi 487 488 if $CommandShortcut; then 489 echo "@echo off" > $UrlName 490 echo "set DBROOTDIR=" >> $UrlName 491 echo "for /F \"tokens=3 delims= \" %%A in ('REG QUERY \"HKLM\\SOFTWARE\\Oracle\\$PRODUCT_NAME\\$PRODUCT_VERSION\" /v RootDirectory') do set DBROOTDIR=%%A" >> $UrlName 492 echo "if ERRORLEVEL 2 goto MISSING" >> $UrlName 493 echo "if not defined DBROOTDIR goto MISSING" >> $UrlName 494 echo "set FN=\"%DBROOTDIR%$Program\"" >> $UrlName 495 echo "if not exist %FN% goto NOTFOUND" >> $UrlName 496 echo "cmd /k \"%DBROOTDIR%$Program\"$CR" >> $UrlName 497 echo "goto END" >> $UrlName 498 echo ":NOTFOUND" >> $UrlName 499 echo "echo" >> $UrlName 500 echo "echo Error: The program does not appear to be installed." >> $UrlName 501 echo "echo" >> $UrlName 502 echo "cmd /k" >> $UrlName 503 echo "goto END" >> $UrlName 504 echo ":MISSING" >> $UrlName 505 echo "echo" >> $UrlName 506 echo "echo NOTE:" >> $UrlName 507 echo "echo The $PRODUCT_NAME version could not be determined." >> $UrlName 508 echo "echo If you are running on Windows 2000, make sure the" >> $UrlName 509 echo "echo REG.EXE program is installed from the Tools disk" >> $UrlName 510 echo "echo" >> $UrlName 511 echo "cmd /k" >> $UrlName 512 echo ":END" >> $UrlName 513 514 Xecho "<File Id=\"File.$Shortname\" " 515 Xecho " LongName=\"$UrlName\" Name=\"$UrlShortName\"" 516 Xecho " Compressed=\"yes\" DiskId=\"1\"" 517 Xecho " src=\"$here_win\\$UrlName\" />" 518 519 Xecho "<Shortcut Id=\"Short.$Shortname\" Directory=\"BerkeleyDbMenu\"" 520 Xecho " Name=\"$Shortname\" LongName=\"$Name\"" 521 Xecho " WorkingDirectory=\"INSTALLDIR\"" 522 Xecho " Target='$TargetFile'" 523 Xecho " Show=\"normal\" />" 524 else 525 Xecho "<Shortcut Id=\"Short.$Shortname\" Directory=\"BerkeleyDbMenu\"" 526 Xecho " Name=\"$Shortname\" LongName=\"$Name\"" 527 Xecho " Target='$TargetFile'" 528 Xecho " Show=\"normal\" />" 529 fi 530 531 532 Xecho - "</Component>" 533 534 Xecho "<ComponentRef Id=\"Links.$Shortname\" />" >> $OutFeatures 535 done 536 return 0 537 ) || Error "Error processing links" || exit 1 538 539 Xecho "</Directory>" 540 Xecho "</DirectoryRef>" 541 Xecho - "</Feature>" >> $OutFeatures 542} 543 544# ProcessOneDirectory(DIRECTORYNAME) 545# Called by ProcessFeatures. 546# Argument is the directory name to process 547# Standard input is cleaned up files.in (dirname is 3rd column) 548# Standard output will be WiX XML Component/File entries 549# 550ProcessOneDirectory() 551{ 552 Dir="$1" 553 grep " ${Dir} " | ( 554 read line 555 while [ "$line" != '' ]; do 556 local feature=`echo "$line" | cut -f 1` 557 local srcfile=`echo "$line" | cut -f 2` 558 local targetdir=`echo "$line" | cut -f 3` 559 local shortname=`echo "$line" | cut -f 4` 560 561 ProcessOneDirectoryFile "$feature" "$srcfile" "$targetdir" "$shortname" || exit 1 562 read line 563 done 564 return 0 565 ) || Error "Error processing directory $Dir" || exit 1 566} 567 568# ProcessOneDirectoryFile(DIRECTORYNAME) 569# Called by ProcessOneDirectory to process a single file in a directory. 570# Standard output will be a single WiX XML Component/File entries 571# 572ProcessOneDirectoryFile() 573{ 574 local feature="$1" 575 local srcfile="$2" 576 local targetdir="$3" 577 local shortname="$4" 578 local base=`basename $srcfile` 579 580 #echo "processing file $srcfile in $feature to directory $targetdir..." >&2 581 582 # Prepend the WIX_DB_TOP unless the source file is absolute 583 584 local root= 585 local checkfile= 586 local wsrcfile= 587 588 case "$srcfile" in 589 /* ) root="" 590 wsrcfile=`cygpath -w $srcfile` 591 checkfile="$srcfile" 592 ;; 593 * ) root="$PRODUCT_BLDDIR/" 594 wsrcfile="WIX_DB_TOP()\\`cygpath -w $srcfile`" 595 checkfile="$PRODUCT_BLDDIR/$srcfile" 596 ;; 597 esac 598 599 # If srcfile ends in / then we'll use tallow to walk the directory 600 case "$srcfile" in 601 */ ) if [ ! -d "$root$srcfile" ]; then 602 Error "$root$srcfile: not a directory" 603 exit 1 604 fi 605 Progress -minor " expanding $root$srcfile..." 606 RunTallow "$root$srcfile" 607 return 0 608 ;; 609 *'*'* ) 610 local dirname=`dirname "$root$srcfile"` 611 RunTallow "$dirname" -df "$base" 612 return 0 613 ;; 614 esac 615 616 if [ "$shortname" = '' ]; then 617 shortname=`GetShortName "$base"` 618 fi 619 ID=`NextId` 620 621 if [ ! -r "$checkfile" ]; then 622 Error "$srcfile: file in feature $feature does not exist" 623 Error " curdir=`pwd`, pathname=$checkfile" 624 exit 1 625 fi 626 627 Xecho "WIX_DB_SET_CURFILE(${base})" 628 Xecho + "<Component Id=\"$feature.$ID\" Location=\"either\" $PERSISTGUID>" 629 Xecho "<File Id=\"${feature}.${ID}\" Name=\"${shortname}\" LongName=\"${base}\" Compressed=\"yes\" KeyPath=\"yes\" DiskId=\"1\" src=\"${wsrcfile}\" />" 630 Xecho - "</Component>" 631 return 0 632} 633 634# ProcessOneFeature(FEATUREPATH, SHORTNAME, DESCRIPTION, OPTS, INDIRECTORYFILE, 635# OUTFEATURE, OUTSET) 636# Called by ProcessFeatures to process a line in the features.in file. 637# The first three arguments are the values of the first three columns: 638# the feature dependency path (e.g. "Java/JavaExamples"), the short 639# name, and a descriptive name. The last argument is the directory 640# file that lists the components. We use the last name of the feature 641# path (e.g. JavaExamples) to locate all components (really directories) 642# named accordingly, so they can be listed as needed parts of the Feature. 643# Standard output will be WiX XML Feature entries. 644# 645ProcessOneFeature() { 646 local featurename="$1" 647 local shortname="$2" 648 local opts="$4" 649 local dirfile="$5" 650 local outfeature="$6" 651 local outset="$7" 652 653 XmlLevel=4 654 local featcount=0 655 local featurestring="" 656 if [ $(SlashCount $featurename) -gt 0 ]; then 657 local parent=`echo $featurename | sed -e 's:/[^/]*$::' -e 's:.*/::'` 658 featurename=`echo $featurename | sed -e 's:^.*/::'` 659 featcount=1 660 Xecho "<FeatureRef Id=\"$parent\">" >> $outfeature 661 fi 662 663 664 # TODO: how to get +default to work? 665 # have tried messing with level="0" (doesn't show it) 666 # InstallDefault=\"source\" (doesn't make a difference) 667 # 668 local leveldebug="Level=\"4\"" 669 local levelparam="Level=\"1\"" 670 local leveldisable="Level=\"0\"" 671 local defparam="InstallDefault=\"source\"" 672 local displayparam="Display=\"expand\"" 673 local params="AllowAdvertise=\"no\"" 674 local regfeature="" 675 676 local descparam="" 677 if [ "$3" != '' ]; then 678 descparam="Description=\"$3\"" 679 fi 680 681 local opt 682 local reqtext="" 683 local isdebugFeature=false 684 for opt in $opts; do 685 case "$opt" in 686 +default ) ;; 687 +required ) params="$params Absent=\"disallow\"" 688 reqtext=" (required)";; 689 +invisible ) displayparam="Display=\"hidden\"" 690 params="$params Absent=\"disallow\"";; 691 +debug ) isdebugFeature=true;; 692 693 * ) Error "features.in: Bad option $opt" 694 exit 1;; 695 esac 696 done 697 698 699# (PBR) 700# I tried to get debugging features to work but I could not. The last thing I 701# tried was to set either ADDSOURCE or INSTALLLEVEL. Neither of these solutions 702# will cause the feature conditions to rerun. The only thing I've found to do 703# that is to rerun CostFinalize. The problem with this is it will re-enable all 704# the options again. I'm keeping the basic framework for +debug support 705 if "$isdebugFeature"; then 706 regfeature="${featurename:1}" 707 echo "regfeature = $regfeature" 708 709 Xecho + "<Feature Id=\"$featurename\" Title=\"$shortname$reqtext\" $descparam $params $displayparam $leveldebug $defparam>" >> $outfeature 710 else 711 Xecho + "<Feature Id=\"$featurename\" Title=\"$shortname$reqtext\" $descparam $params $displayparam $levelparam $defparam>" >> $outfeature 712 fi 713 714 715 grep 'Component.*Id="'$featurename'[.0-9]*"' "$dirfile" | sed -e 's/\(Id="[^"]*"\).*/\1 \/>/' -e 's/Component /ComponentRef /' >> $outfeature 716 717 # Create a separate subfeature for any environment variables 718 # associated with the main feature. 719 # The <Condition> stuff is the magic that enables/disables 720 # setting the environment variables depending on the check box property. 721 722 Xecho + "<Feature Id=\"env.$featurename\" Title=\"env vars\" $params Display=\"hidden\" $levelparam $defparam>" >> $outfeature 723 724 Xecho "<Condition $leveldisable><![CDATA[EnvironmentSetCheck<>1]]></Condition>" >> $outfeature 725 Xecho "<Condition $levelparam><![CDATA[EnvironmentSetCheck=1]]></Condition>" >> $outfeature 726 grep 'Component.*Id="env\.'$featurename'[.0-9]*"' "$dirfile" | sed -e 's/\(Id="[^"]*"\).*/\1 \/>/' -e 's/Component /ComponentRef /' >> $outfeature 727 728 Xecho - "</Feature>" >> $outfeature 729 Xecho - "</Feature>" >> $outfeature 730 731 while [ "$featcount" -gt 0 ]; do 732 Xecho - "</FeatureRef>" >> $outfeature 733 featcount=$(($featcount - 1)) 734 done 735 736 # Append the name to the feature list if it is to be installed. 737 # This publish fragment gets 'executed' when leaving the 738 # dialog to select features. Note that we have to quote 739 # the comma for m4 (`,') since this appears in a macro usage. 740 # 741 742# (PBR) 743# This code sets ADDSOURCE to show which debug options to include. This does not work 744 # If this is a debug feature, only turn on the value if the parent is on 745 if "$isdebugFeature"; then 746 regfeature="${featurename:1}" 747 748# Xecho "<Publish Property=\"ADDSOURCE\" Value=\"[ADDSOURCE]\`,'\">" >> $outset 749# Xecho " <![CDATA[&${regfeature} = 3 AND DebugCheck=\"yes\" AND ADDSOURCE <> NULL]]></Publish>" >> $outset 750 751# Xecho "<Publish Property=\"ADDSOURCE\" Value=\"[ADDSOURCE]${featurename}\">" >> $outset 752# Xecho " <![CDATA[&${regfeature} = 3 AND DebugCheck=\"yes\"]]></Publish>" >> $outset 753 754 Xecho "<Publish Property=\"FeatureList\" Value=\"[FeatureList]\`,' ${shortname}\">" >> $outset 755 Xecho " <![CDATA[&${regfeature} = 3 AND DebugCheck=\"yes\"]]></Publish>" >> $outset 756 757 else 758 Xecho "<Publish Property=\"FeatureList\" Value=\"[FeatureList]\`,' ${shortname}\">" >> $outset 759 Xecho " <![CDATA[&${featurename} = 3]]></Publish>" >> $outset 760 fi 761} 762 763# ProcessOneEnv(FEATURE, ENVNAME, ENVVALUE, OPTS, OUTDIRS, OUTSET) 764# Called by ProcessFeatures to process a line in the environment.in file. 765# The four arguments are the values of the four columns. 766# The output will be into two files: 767# OUTDIRS: WiX XML Component entries, that contain environment values. 768# This controls the actual setting of the variables. 769# OUTSET: WiX XML to set the installer variables if an env variable 770# is set or a feature selected. 771# 772ProcessOneEnv() { 773 local feature="$1" 774 local envname="$2" 775 local envvalue="$3" 776 local opts="$4" 777 local outdirs="$5" 778 local outset="$6" 779 780 # Make the path uniform. 781 # echo "c:\Program Files\...\/Lib/Hello" | sed -e 's:\\/:\\:' -e 's:/:\\:g` 782 # This produces c:\Program Files\...\Lib\Hello 783 case "$envvalue" in 784 /* ) envvalue=`echo "$envvalue" | sed -e 's:^/::'` 785 esac 786 787 local path="[INSTALLDIR]$envvalue" 788 789 local opt 790 part="last" 791 for opt in $opts; do 792 case "$opt" in 793 +first ) part="first";; 794 +last ) part="last";; 795 * ) Error "environment.in: Bad option $opt" 796 exit 1;; 797 esac 798 done 799 800 # Generate the OUTDIRS fragment 801 # This looks like: 802 # 803 # <Component Id="env.CoreAPI.43" Guid="4B75755F-1129-292C-3434-238410000247"> 804 # <Environment Id="env.44" Name="+-LIB" Action="set" 805 # Permanent="no" Part="first" Value="[INSTALLDIR]Lib" /> 806 # </Component> 807 # 808 # Having a unique guid makes uninstall work. 809 # Note: We really want these installed as System rather than 810 # User vars (using the System="yes" tag), but only if user 811 # installs for *all* users. There is no convenient way to 812 # do that, so we leave them as default (User variables). 813 814 815 XmlLevel=4 816 local Id=`NextId` 817 Xecho "WIX_DB_SET_CURFILE(${envname})" >> $outdirs 818 Xecho + "<Component Id=\"env.$feature.$Id\" $PERSISTGUID>" >> $outdirs 819 Id=`NextId` 820 821 Xecho "<Environment Id=\"env.$Id\" Name=\"+-$envname\" Action=\"set\"" >> $outdirs 822 Xecho " Permanent=\"no\" Part=\"$part\" Value=\"$path\" />" >> $outdirs 823 824 Xecho "</Component>" >> $outdirs 825 826 # Generate the OUTSET fragment 827 # This looks like: 828 # 829 # <Publish Property="CLASSPATHValue" Value="[INSTALLDIR]Lib/db.jar;[CLASSPATHValue]"> 830 # <![CDATA[&JavaAPI = 3]]></Publish> 831 # <Publish Property="CLASSPATHEscValue" Value="[INSTALLDIR]Lib/db.jar;[CLASSPATHEscValue]"> 832 # <![CDATA[&JavaAPI = 3]]></Publish> 833 # 834 # This is equivalent to pseudocode: 835 # if (InstallFeature(JavaAPI)) { 836 # Prepend CLASSPATHValue with "Lib/db.jar;" 837 # Prepend CLASSPATHEscValue with "Lib/db.jar;" 838 # } 839 # 840 XmlLevel=4 841 Xecho "<Publish Property=\"${envname}Value\" Value=\"[INSTALLDIR]${envvalue};[${envname}Value]\">" >> $outset 842 Xecho " <![CDATA[&${feature} = 3]]></Publish>" >> $outset 843 844 Xecho "<Publish Property=\"${envname}EscValue\" Value=\"[INSTALLDIR]${envvalue};[${envname}EscValue]\">" >> $outset 845 Xecho " <![CDATA[&${feature} = 3]]></Publish>" >> $outset 846 847 848} 849 850# CreateProperty(ID, VALUE) 851# Generate a <Property...> tag on the stdout 852CreateProperty() { 853 Xecho "<Property Id=\"$1\" Hidden=\"yes\"><![CDATA[$2]]></Property>" 854} 855 856# ProcessTagProperties(OUTPROPS) 857# Generate some identification tags as properties. 858# This will let us look at an installer and figure out 859# when it was built, etc. 860ProcessTagProperties() { 861 local outprops="$1" 862 local insdate=`date` 863 XmlLevel=4 864 865 CreateProperty _DB_MSI_INSTALLER_DATE "$insdate" >> $outprops 866 CreateProperty _DB_MSI_PRODUCT_NAME "$PRODUCT_NAME" >> $outprops 867 CreateProperty _DB_MSI_PRODUCT_VERSION "$PRODUCT_VERSION" >> $outprops 868 CreateProperty ARPCOMMENTS "Installer for $PRODUCT_NAME $PRODUCT_VERSION built on $insdate" >> $outprops 869} 870 871# ProcessEnv(INENVFILE, INBATFILE, OUTPROPS, OUTSET, OUTSHOW) 872# We generate some Property magic to show the user what is set. 873# 874ProcessEnv() { 875 InEnv="inenv.tmp"; CleanInputFile "$1" "$InEnv" 3 4 876 inbat="$2" 877 outprops="$3" 878 outset="$4" 879 outshow="$5" 880 881 # Get a list of the environment variables 882 local envvar 883 local envvars=`cut -f 2 < $InEnv | sort | uniq` 884 885 # For each environment var, create lines that declare 886 # a pair of properties in the envprops.wixinc file like: 887 # 888 # <Property Id="CLASSPATHValue" Hidden="yes"></Property> 889 # <Property Id="CLASSPATHEscValue" Hidden="yes"></Property> 890 # 891 # And create lines in the envset.wixinc file like: 892 # 893 # <Publish Property="CLASSPATHValue" Value="%CLASSPATH%"> 894 # <![CDATA[1]]></Publish> 895 # <Publish Property="CLASSPATHEscValue" Value="\\%CLASSPATH\\%"> 896 # <![CDATA[1]]></Publish> 897 # 898 # More will be added to that file later. 899 # Then, create lines in the envshow.wixinc file like: 900 # 901 # <Control Id="CLASSPATHText" Type="Text" 902 # X="23" Width="316" PARTIALHEIGHT(10, 2) 903 # TabSkip="no" Text="CLASSPATH:" /> 904 # 905 # <Control Id="CLASSPATHValueText" Type="Text" 906 # X="37" Width="316" PARTIALHEIGHT(20, 7) 907 # TabSkip="no" Text="[CLASSPATHValue]" /> 908 909 for envvar in $envvars; do 910 XmlLevel=4 911 CreateProperty "${envvar}Value" "" >> $outprops 912 CreateProperty "${envvar}EscValue" "" >> $outprops 913 914 XmlLevel=4 915 Xecho "<Publish Property=\"${envvar}Value\" Value=\"%${envvar}%\">" >> $outset 916 Xecho " <![CDATA[1]]></Publish>" >> $outset 917 Xecho "<Publish Property=\"${envvar}EscValue\" Value=\"\\%${envvar}\\%\">" >> $outset 918 Xecho " <![CDATA[1]]></Publish>" >> $outset 919 920 XmlLevel=4 921 Xecho "<Control Id=\"${envvar}Text\" Type=\"Text\"" >> $outshow 922 Xecho " X=\"23\" Width=\"316\" PARTIALHEIGHT(10, 2)" >> $outshow 923 Xecho " TabSkip=\"no\" Text=\"${envvar}:\" />" >> $outshow 924 925 Xecho "<Control Id=\"${envvar}ValueText\" Type=\"Text\"" >> $outshow 926 Xecho " X=\"37\" Width=\"316\" PARTIALHEIGHT(20, 7)" >> $outshow 927 Xecho " TabSkip=\"no\" Text=\"[${envvar}Value]\" />" >> $outshow 928 929 done 930 931 # Create the dbvars.bat file from the .bat template file 932 # TODO: the bat template file currently knows the variables 933 # and their values, it should get them from the environment.in 934 935 RunM4 <"$inbat" >"$PRODUCT_STAGE/dbvars.bat" || Error "m4 failed" || exit 1 936} 937 938 939# CleanInputFile(INFILENAME, OUTFILENAME, MINELEMENTS, MAXELEMENTS) 940# A filter to preprocess and validate input files. 941# We end up without comment lines, a single tab between elements, 942# and a trailing tab. 943# Also some selected shell variables are expanded for convenience. 944# We verify that each line has the number of elements that fall within 945# the given min and max. 946# 947CleanInputFile() { 948 sed \ 949 -e 's/#.*//' \ 950 -e 's/ * / /g' \ 951 -e 's/ */ /g' \ 952 -e '/^[ ]*$/d' \ 953 -e 's/$/ /' \ 954 -e 's/ */ /g' \ 955 -e 's:\${PRODUCT_VERSION}:'"${PRODUCT_VERSION}":g \ 956 -e 's:\${PRODUCT_MAJOR}:'"${PRODUCT_MAJOR}":g \ 957 -e 's:\${PRODUCT_MINOR}:'"${PRODUCT_MINOR}":g \ 958 -e 's:\${PRODUCT_PATCH}:'"${PRODUCT_PATCH}":g \ 959 -e 's:\${PRODUCT_MAJMIN}:'"${PRODUCT_MAJMIN}":g \ 960 -e 's:\${PRODUCT_STAGE}:'"${PRODUCT_STAGE}":g \ 961 -e 's:\${PRODUCT_SHARED_WINMSIDIR}:'"${PRODUCT_SHARED_WINMSIDIR}":g \ 962 -e 's/^[\r \t]*$//' \ 963 < "$1" > "$2" 964 965 # count tabs on each line 966 sed -e 's/[^\t]//g' -e 's/[\t]/x/g' < "$2" | ( 967 read line 968 linecount=1 969 while [ "$line" != '' ]; do 970 chars=`echo "$line" | wc -c` 971 chars=$(($chars - 1)) # Remove newline 972 if [ "$chars" -lt "$3" -o "$chars" -gt "$4" ]; then 973 Error "$1: Input file error on or after line $linecount" 974 fi 975 read line 976 linecount=$(($linecount + 1)) 977 done 978 ) 979} 980 981# StripDoubleQuotes() 982# In some input files, we allow double quotes around 983# multi-word strings for readability. We strip them 984# here from standard input and write to standard output. 985# We only expect them at the beginning and end. 986# 987StripDoubleQuotes() { 988 sed -e 's/^"//' -e 's/"$//' 989} 990 991# IndentXml(PLUSMINUS_ARG) 992# A global variable $XmlLevel is kept for the indent level. 993# Every call creates blank output that matches the indent level. 994# In addition, with a '-' argument, the indent level 995# decrements by one before printing. 996# With a '+', the indent level increments after printing. 997# This is generally just used by Xecho 998# 999XmlLevel=0 1000IndentXml() { 1001 if [ "$1" = '-' -a $XmlLevel != 0 ]; then 1002 XmlLevel=$(($XmlLevel - 1)) 1003 fi 1004 local idx=0 1005 while [ "$idx" != "$XmlLevel" ]; do 1006 echo -n ' ' 1007 idx=$(($idx + 1)) 1008 done 1009 if [ "$1" = '+' ]; then 1010 XmlLevel=$(($XmlLevel + 1)) 1011 fi 1012} 1013 1014# Xecho [ - | + ] ... 1015# echoes arguments (like) echo, except that the output 1016# is indented for XML first. If +, the indentation changes 1017# after printing, if -, the indentation changes before printing. 1018# 1019Xecho() 1020{ 1021 local xarg= 1022 if [ "$1" = '-' -o "$1" = '+' ]; then 1023 xarg="$1" 1024 shift 1025 fi 1026 IndentXml $xarg 1027 echo "$@" 1028} 1029 1030# SlashCount(PATH) 1031# Returns the number of slashes in its argument 1032# Note, we are relying on some advanced 1033# features of bash shell substitution 1034# 1035SlashCount() 1036{ 1037 local allslash=`echo "$1" | sed -e 's:[^/]*::g'` 1038 echo "${#allslash}" 1039} 1040 1041# ProcessDirTransition(PREVDIR, NEXTDIR) 1042# Used by ProcessFeatures to create the parts 1043# of an WiX <Directory> heirarchy (on stdout) needed to 1044# transition from directory PREVDIR to NEXTDIR. 1045# This may include any needed </Directory> entries as well. 1046# For example, ProcessDirTransition /Bin/Stuff /Bin/Foo/Bar 1047# produces: 1048# </Directory> ...to go up one from 'Stuff' 1049# <Directory Foo> 1050# <Directory Bar> 1051# 1052ProcessDirTransition() { 1053 local p="$1" 1054 local n="$2" 1055 if [ "$p" = '' ]; then p=/; fi 1056 if [ "$n" = '' ]; then n=/; fi 1057 local nextdir="$2" 1058 1059 # The number of slashes in $p is the current directory level. 1060 XmlLevel=$(($(SlashCount $p) + 4)) 1061 1062 while [ "$p" != / ]; do 1063 if [ "${n#${p}}" != "$n" ]; then 1064 break 1065 fi 1066 1067 # go up one level, and keep $p terminated with a / 1068 p=`dirname $p` 1069 case "$p" in 1070 */ ) ;; 1071 * ) p=$p/;; 1072 esac 1073 Xecho - "</Directory>" 1074 done 1075 n=${n#${p}} 1076 while [ "$n" != '' ]; do 1077 local dirname=`echo $n | sed -e 's:/.*::'` 1078 local cleanname=`CleanFileName "$dirname"` 1079 local shortname=`GetShortName "$cleanname"` 1080 local dirid=`NextId` 1081 1082 local larg="" 1083 if [ "${shortname}" != "${dirname}" ]; then 1084 larg="LongName=\"${dirname}\"" 1085 fi 1086 Xecho + "<Directory Id=\"${cleanname}Dir.$dirid\" Name=\"${shortname}\" $larg>" 1087 1088 n=`echo $n | sed -e 's:^[^/]*/::'` 1089 done 1090 1091 Xecho "WIX_DB_SET_CURDIR($nextdir)" # Tell the m4 macro what the current dir is 1092} 1093 1094# SetupErrorLog() 1095# Given the global variable ERRORLOG for the name of the 1096# error output file, do any setup required to make that happen. 1097# 1098SetupErrorLog() { 1099 1100 # Before we start to use ERRORLOG, we get a full pathname, 1101 # since the caller may change directories at times. 1102 case "$ERRORLOG" in 1103 /* ) ;; 1104 *) ERRORLOG=`pwd`"/$ERRORLOG" ;; 1105 esac 1106 1107 rm -f $ERRORLOG 1108 1109 # File descriptor tricks. 1110 # Duplicate current stderr to 15, as we'll occasionally 1111 # need to report progress to it. Then, redirect all 1112 # stderr from now on to the ERRORLOG. 1113 # 1114 exec 15>&2 1115 exec 2>>$ERRORLOG 1116} 1117 1118# RequireCygwin 1119# Cygwin does not install certain needed components by default. 1120# Check to make sure that everything needed by the script 1121# and functions is here. 1122# 1123RequireCygwin() { 1124 Progress -minor "checking for Cygwin..." 1125 RequireFileInPath PATH "$PATH" m4 1126 RequireFileInPath PATH "$PATH" gcc 1127 RequireFileInPath PATH "$PATH" make 1128 RequireFileInPath PATH "$PATH" unzip 1129 RequireFileInPath PATH "$PATH" bc 1130 RequireFileInPath PATH "$PATH" openssl # needed for MD5 hashing 1131} 1132 1133# RequireJava() 1134# A java SDK (with include files) must be installed 1135# 1136RequireJava() { 1137 Progress -minor "checking for Java..." 1138 RequireFileInPath INCLUDE "$INCLUDE" jni.h 1139 RequireFileInPath INCLUDE "$INCLUDE" jni_md.h 1140 RequireFileInPath PATH "$PATH" jar.exe 1141 RequireFileInPath PATH "$PATH" javac.exe 1142} 1143 1144# RequireTcl() 1145# A Tcl SDK (with compatible .lib files) must be installed 1146# 1147RequireTcl() { 1148 Progress -minor "checking for Tcl..." 1149 RequireFileInPath INCLUDE "$INCLUDE" tcl.h 1150 RequireFileInPath LIB "$LIB" tcl84g.lib 1151 RequireFileInPath LIB "$LIB" tcl84.lib 1152} 1153 1154# RequireWix() 1155# WiX must be installed 1156# 1157RequireWix() { 1158 Progress -minor "checking for WiX..." 1159 RequireFileInPath PATH "$PATH" candle.exe 1160 RequireFileInPath PATH "$PATH" light.exe 1161 RequireFileInPath PATH "$PATH" tallow.exe 1162} 1163 1164# RequirePerl() 1165# Perl must be installed 1166# 1167RequirePerl() { 1168 Progress -minor "checking for Perl..." 1169 RequireFileInPath PATH "$PATH" perl.exe 1170} 1171 1172# RequirePython() 1173# Python (and include files) must be installed 1174# 1175RequirePython() { 1176 Progress -minor "checking for Python..." 1177 RequireFileInPath INCLUDE "$INCLUDE" Python.h 1178 RequireFileInPath PATH "$PATH" python.exe 1179} 1180 1181# CreateDbPerl() 1182# Build Perl interface (for Berkeley DB only). 1183# 1184CreateDbPerl() { 1185 1186 # First build Berkeley DB using cygwin, as that version is 1187 # needed for the Perl build 1188 local here=`pwd` 1189 Progress "building using Cygwin tools (needed for perl)" 1190 cd "${PRODUCT_DBBUILDDIR}" 1191 insdir="${PRODUCT_STAGE}/install_unix" 1192 ../dist/configure --prefix="$insdir" >>$ERRORLOG || exit 1 1193 make install >>$ERRORLOG || exit 1 1194 1195 Progress "building perl" 1196 cd ../perl/BerkeleyDB 1197 BERKELEYDB_INCLUDE="$insdir/installed_include" BERKELEYDB_LIB="$insdir/lib" \ 1198 perl Makefile.PL >>$ERRORLOG || exit 1 1199 make >>$ERRORLOG 1200 cd $here 1201} 1202 1203# CreateWindowsSystem() 1204# Copy Window system files 1205# 1206CreateWindowsSystem() { 1207 local here=`pwd` 1208 Progress "Copy Window system files..." 1209 cd "${PRODUCT_SUB_BLDDIR}" 1210 cp -f $SYSTEMROOT/system32/msvcr71.dll build_windows/Release/ || exit 1 1211 cp -f $SYSTEMROOT/system32/msvcp71.dll build_windows/Release/ || exit 1 1212 cp -f $SYSTEMROOT/system32/msvcr71.dll build_windows/Debug/ || exit 1 1213 cp -f $SYSTEMROOT/system32/msvcp71.dll build_windows/Debug/ || exit 1 1214 cd $here 1215} 1216 1217# CreateInclude(DIR, FILES) 1218# Create an include directory populated with the files given 1219# 1220CreateInclude() { 1221 1222 local incdir="$1" 1223 shift 1224 1225 Progress "creating the "$incdir" directory..." 1226 rm -rf "$incdir" 1227 mkdir "$incdir" || exit 1 1228 cp -r "$@" "$incdir" 1229} 1230 1231# CreateWindowsBuild() 1232# Do the windows build as defined by the winbuild.bat file 1233# 1234CreateWindowsBuild() { 1235 local here=`pwd` 1236 Progress "building using Windows tools..." 1237 cd "${PRODUCT_SUB_BLDDIR}" || exit 1 1238 1239 # Before starting, copy any installer tools here. 1240 # This makes building these tools straightforward 1241 # and the results are left in the build directory. 1242 # 1243 cp -r ${PRODUCT_SHARED_WINMSIDIR}/instenv . 1244 1245 # We create a wbuild.bat file, which is essentially 1246 # identical, except it has the carriage returns added. 1247 # This allows us to use our favorite editors on winbuild.bat . 1248 # 1249 sed -e 's/$//' < ${PRODUCT_STAGE}/../winbuild.bat | tr '\001' '\015' > wbuild.bat 1250 # TODO: Needed? 1251 rm -f build_windows/Berkeley_DB.sln 1252 rm -f winbld.out winbld.err 1253 touch winbld.out winbld.err 1254 echo "Build output and errors are collected in" >> $ERRORLOG 1255 echo " winbld.{out,err} until the build has completed." >> $ERRORLOG 1256 cmd.exe /x /c call wbuild.bat 1257 status=$? 1258 cat winbld.out >> $ERRORLOG 1259 if [ -s winbld.err -o "$status" != 0 ]; then 1260 cat winbld.err >> $ERRORLOG 1261 Error "Errors during windows build" 1262 exit 1 1263 fi 1264 cd $here 1265} 1266 1267# CreateSources(SOURCESDIR,DOCDIR...) 1268# Create the sources directory, ignoring things in the docdirs 1269# 1270CreateSources() { 1271 local sources="$1" 1272 1273 Progress "creating the Sources directory in $sources..." 1274 rm -rf "$sources" 1275 cp -r ${PRODUCT_SRCDIR} "$sources" || exit 1 1276} 1277 1278# Usage() 1279# Show the usage for this script. 1280# 1281Usage() 1282{ 1283 echo "Usage: s_winmsi [ options ]" >&2 1284 echo "Options: " >&2 1285 echo " -input file use file rather than ${PRODUCT_ZIP_FILEFMT}" >&2 1286 echo " where X.Y.Z is defined by ../RELEASE" >&2 1287 echo " -output file use file rather than ${PRODUCT_MSI_FILEFMT}" >&2 1288 echo " where X.Y.Z is defined by ../RELEASE" >&2 1289 echo " -usebuild DIR use DIR for exes, DLLs, etc. " >&2 1290 echo " rather than building from scratch" >&2 1291 echo " -preserve preserve the winmsi/msi_staging directory" >&2 1292 echo " -skipgen skip generating m4 include files" >&2 1293} 1294 1295# SetupOptions() 1296# Parse command line options and set global variables as indicated below. 1297# 1298SetupOptions() { 1299 OPT_USEBUILD= 1300 OPT_PRESERVE=false 1301 OPT_INFILE= 1302 OPT_OUTFILE= 1303 OPT_SKIPGEN=false 1304 while [ "$#" -gt 0 ]; do 1305 arg="$1"; shift 1306 case "$arg" in 1307 -usebuild ) OPT_USEBUILD="$1"; shift ;; 1308 -skipgen ) OPT_SKIPGEN=true ;; 1309 -preserve ) OPT_PRESERVE=true;; 1310 -input ) OPT_INFILE="$1"; shift ;; 1311 -output ) OPT_OUTFILE="$1"; shift ;; 1312 * ) 1313 echo "ERROR: Unknown argument '$arg' to s_winmsi" >&2 1314 Usage 1315 exit 1 1316 ;; 1317 esac 1318 done 1319 if [ "$OPT_INFILE" = '' -o ! -f "$OPT_INFILE" ]; then 1320 echo "$OPT_INFILE: not found" 1321 exit 1 1322 fi 1323} 1324 1325# CreateStage() 1326# Create the staging area 1327# 1328CreateStage() { 1329 Progress "creating staging area..." 1330 if [ "$PRODUCT_STAGE" = '' ]; then 1331 Error "PRODUCT_STAGE not set" 1332 exit 1 1333 fi 1334 if ! $OPT_PRESERVE; then 1335 trap 'rm -rf ${PRODUCT_STAGE} ; exit 0' 0 1 2 3 13 15 1336 fi 1337 rm -rf ${PRODUCT_STAGE} || exit 1 1338 mkdir ${PRODUCT_STAGE} || exit 1 1339 1340 cd ${PRODUCT_STAGE} 1341 1342 Progress "extracting $OPT_INFILE..." 1343 unzip -q ../../$OPT_INFILE || exit 1 1344 1345 if [ ! -d $PRODUCT_LICENSEDIR ]; then 1346 Error "$OPT_INFILE: no top level $PRODUCT_LICENSEDIR directory" 1347 exit 1 1348 fi 1349} 1350 1351# CreateLicenseRtf(LICENSEIN, LICENSERTF) 1352# From a text LICENSE file, create the equivalent in .rtf format. 1353# 1354CreateLicenseRtf() { 1355 local licensein="$1" 1356 local licensertf="$2" 1357 1358 if [ ! -f "$licensein" ]; then 1359 Error "License file $licensein: does not exist" 1360 exit 1 1361 fi 1362 Progress "creating ${licensertf}..." 1363 1364 # Build a list of references to components ids (i.e. directories) 1365 # that are listed in the .wxs file. This is needed to refer to 1366 # all of the source (sadly it appears there is no better way!) 1367 # 1368 if ! grep '^=-=-=-=' $licensein > /dev/null; then 1369 Error "LICENSE has changed format, this script must be adapted" 1370 exit 1 1371 fi 1372 1373 sed -e '1,/^=-=-=-=-=/d' < $licensein | MakeRtf > $licensertf 1374} 1375 1376 1377# CreateMsi(INFILE,WXSFILE,MSIFILE) 1378# Do the final creation of the output .MSI file. 1379# It is assumed that all *.wixinc files are now in place. 1380# INFILE is an absolute name of the m4 input WiX file. 1381# WXSFILE is a short (basename) of the postprocessed WiX file, 1382# after macro expansion, it will be left in staging directory. 1383# MSIFILE is a short (basename) of the output .MSI name 1384# 1385CreateMsi() { 1386 local infile="$1" 1387 local wxs="$2" 1388 local msifile="$3" 1389 local o=`echo "$wxs" | sed -e 's/[.]wxs$//' -e 's/$/.wixobj/'` 1390 local tmpfile="dbcore.wxs.tmp" 1391 1392 rm -f $o $wxs 1393 1394 # Preprocess the ${PROD}wix.in file, adding the things we need 1395 # 1396 Progress "Running m4 to create $wxs..." 1397 RunM4 < "$infile" > "$PRODUCT_STAGE/$wxs" || Error "m4 failed" || exit 1 1398 # Remove any stray "PUT-GUID-HERE" tags. 1399 # They are added by some recent versions of WiX, including 2.0.5805.0 1400 # The scripts already insert valid GUIDs. 1401 sed -e 's/ Guid="PUT-GUID-HERE" / /' < "$wxs" > "$tmpfile" 1402 rm -f "$wxs" 1403 mv "$tmpfile" "$wxs" 1404 1405 local here=`pwd` 1406 cd "$PRODUCT_STAGE" 1407 rm -f "$o" "$msifile" 1408 Progress "compiling $wxs..." 1409 candle -w0 $wxs >> $ERRORLOG || Error "candle (compiler) failed" || exit 1 1410 1411 Progress "linking .msi file..." 1412 light -o "$msifile" $o >> $ERRORLOG || Error "light (linker) failed" || exit 1 1413 (rm -f "../../$msifile" && mv "$msifile" ../..) >> $ERRORLOG || exit 1 1414 cd $here 1415} 1416 1417# CreateWixIncludeFiles() 1418# Do all processing of input files to produce 1419# the include files that we need to process the Wix input file. 1420# 1421CreateWixIncludeFiles() { 1422 local here=`pwd` 1423 cd "$PRODUCT_STAGE" 1424 # Touch all the wix include files in case any end up empty. 1425 touch directory.wixinc features.wixinc envprops.wixinc \ 1426 envset.wixinc envshow.wixinc links.wixinc 1427 1428 Progress "tagging the installer..." 1429 ProcessTagProperties envprops.wixinc 1430 1431 Progress "processing environment..." 1432 ProcessEnv ../environment.in ../dbvarsbat.in envprops.wixinc envset.wixinc envshow.wixinc 1433 1434 Progress "processing features and files..." 1435 ProcessFeatures ../files.in ../features.in ../environment.in \ 1436 directory.wixinc features.wixinc \ 1437 envset.wixinc 1438 1439 Progress "processing links..." 1440 ProcessLinks ../links.in features.wixinc > links.wixinc 1441 cd $here 1442} 1443