Ticket #4680: installOptionalPackage.sh

File installOptionalPackage.sh, 14.5 KB (added by mmadia, 10 years ago)

Added disclaimer

Line 
1#!/bin/sh
2#
3# Copyright (c) 2009 Haiku Inc. All rights reserved.
4# Distributed under the terms of the MIT License.
5#
6# Authors:
7#       Matt Madia, mattmadia@gmail.com
8#
9# Synopsis:
10#   Provides a controlled mechanism for end-users to install certain pre-built
11#   OptionalPackages. The script will determine the host information: the
12#   default GCC, availability of secondary GCC libs, and revision. Using this
13#   information, the user will be limited to the appropriate OptionalPackages
14#   that were available for that specific revision.
15#
16# Disclaimer:
17#   This is a temporary solution for installing Optional Packages.
18#   In time, there will be an official package manager.
19#   See these URL's for info on the in-development package manager.
20#     http://dev.haiku-os.org/wiki/PackageManagerIdeas
21#     http://dev.haiku-os.org/wiki/PackageFormat
22#
23# Usage: ./installOptionalPackage.sh [-l] [-a]
24# -l      List installable packages
25# -a      Add a package and all of its dependencies
26
27
28declare -a packagesToInstall   
29declare -a availablePackages
30# Some Packages cannot be installed,
31# as they require either the source code or compiled binaries
32declare -a packageBlackList=( 'Bluetooth' 'Development' 'DevelopmentMin' \
33    'DevelopmentBase' 'P7zip' 'UserlandFS' 'Welcome')
34
35
36function CreateInstallerScript()
37{
38    # This function will create a secondary script, containing all of the
39    # information needed to install the optional package and its dependencies
40
41#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
42    cat << EOF > ${tmpDir}/install-optpkg.sh
43#!/bin/sh
44
45tmpDir=${tmpDir}
46HAIKU_GCC_VERSION[1]=${HAIKU_GCC_VERSION[1]}
47isHybridBuild=${isHybridBuild}
48TARGET_ARCH=${TARGET_ARCH}
49HAIKU_IMAGE_HOST_NAME=`uname -n`
50$urlLine
51$sslPkgLine
52$sslUrlLine
53declare -a functionArgs
54
55
56function ParseFunctionArguments()
57{
58    # ParseFunctionArguments <args>
59    # Parse arguments for Jam wrapper functions into an array.
60    IN="\$@"
61    OIFS=\$IFS
62    IFS=":"
63   
64    local count=0
65    functionArgs=( )
66    for x in \$IN
67    do
68        functionArgs[\${count}]="\${x}"
69        ((count++))
70    done
71    IFS=\$OIFS
72}
73
74
75function TrimLeadingSpace()
76{
77    # TrimLeadingSpace <variable name>
78    eval local text='\$'"\$1"
79    local _outvar="\$1"
80
81    local length=\${#text}
82    ((length--))
83    if [ "\${text:0:1}" == ' ' ] ; then
84        text=\${text#' '}
85    fi
86
87    eval \$_outvar="'\$text'"
88}
89
90
91function TrimEndingSpace()
92{
93    # TrimEndingSpace <variable name>
94    eval local text='\$'"\$1"
95    local _outvar="\$1"
96
97    local length=\${#text}
98    ((length--))
99    if [ "\${text:\$length}" == ' ' ] ; then
100        text=\${text%' '}
101    fi
102
103    eval \$_outvar="'\$text'"
104}
105
106
107function Exit()
108{
109    # Exit <message>
110    # Wrapper for Jam rule
111    echo "\$@"
112    exit 1
113}
114
115
116function InstallOptionalHaikuImagePackage()
117{
118    # InstallOptionalHaikuImagePackage package : url : dirTokens : isCDPackage
119    # Wrapper for Jam rule
120    echo "Installing \$1 ..."
121    cd \$tmpDir
122   
123    zipFile=\`echo \$3 | sed -s "s/http.*\///"\`
124    if ! [ -f \$zipFile ] ; then
125        echo "Downloading \$3 ..."
126        wget -nv \$3
127    fi
128   
129    local dirTokens='/boot'
130    local count=4
131    local i=0
132    for possibleToken in "\$@" ; do
133        if [ \$i -lt \$count ] ; then
134            ((i++))
135        else
136            ((i++))
137            if [ "\$possibleToken" != ':' ] ; then         
138                dirTokens=\${dirTokens}/\$possibleToken
139            else
140                break
141            fi
142        fi
143    done
144    echo "Unzipping \$zipFile ..."
145    unzipDir="\${dirTokens}"
146    unzip -q -d "\$unzipDir" "\$zipFile"
147    # TODO should .OptionalPackageDescription be added to AboutSystem?
148    if [ -f '/boot/.OptionalPackageDescription' ] ; then
149        rm '/boot/.OptionalPackageDescription'
150    fi
151    rm "\$zipFile"
152}
153
154
155function AddSymlinkToHaikuImage()
156{
157    # AddSymlinkToHaikuImage <dir tokens> : <link target> [ : <link name> ]
158    # Wrapper for Jam rule
159    ParseFunctionArguments "\$@"
160   
161    local dirTokens="/boot/\${functionArgs[0]}"
162    TrimLeadingSpace dirTokens
163    TrimEndingSpace dirTokens
164    dirTokens=\${dirTokens//' '/\/}
165   
166    local linkTarget="\${functionArgs[1]}"
167    TrimLeadingSpace linkTarget
168    TrimEndingSpace linkTarget
169   
170    local linkName="\${functionArgs[2]}"
171    TrimLeadingSpace linkName
172    TrimEndingSpace linkName
173           
174    if [ "\${linkName}" == '' ] ; then
175        ln -sf "\${linkTarget}" -t "\${dirTokens}"
176    else
177        ln -sf "\${linkTarget}" "\${dirTokens}/\${linkName}"
178    fi
179}
180
181
182function AddUserToHaikuImage()
183{
184    # AddUserToHaikuImage user : uid : gid : home : shell : realName
185    # Wrapper for Jam rule 
186    ParseFunctionArguments "\$@"
187   
188    local user=\${functionArgs[0]}
189    local uid=\${functionArgs[1]}
190    local gid=\${functionArgs[2]}
191    local home=\${functionArgs[3]}
192    local shell=\${functionArgs[4]}
193    local realName=\${functionArgs[5]}
194   
195    passwdLine="\${user}:x:\${uid}:\${gid}:\${realName}:\${home}:\${shell}"
196    passwdLine=\${passwdLine//' :'/':'}
197    passwdLine=\${passwdLine//': '/':'}
198   
199    local length=\${#passwdLine}
200    ((length--))
201    if [ "\${passwdLine:\$length}" == ' ' ] ; then
202        passwdLine=\${passwdLine%' '}
203    fi
204   
205    passwdFile="\`finddir B_COMMON_ETC_DIRECTORY\`/passwd"
206    touch \${passwdFile}
207   
208    local userExists=1
209    while read line ; do
210        if [ "\${passwdLine}" == "\${line}" ] ; then
211            userExists=0
212        fi
213    done < \${passwdFile}
214   
215    if [ \$userExists -ge 1 ] ; then
216        echo "\${passwdLine}" >> \${passwdFile}
217    fi
218}
219
220
221EOF
222#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
223    cat ${tmpDir}/optpkg.stage2 >> ${tmpDir}/install-optpkg.sh
224    rm ${tmpDir}/optpkg.stage2
225}
226
227
228function GetBuildFile()
229{
230    # GetBuildFile <file>
231    # Downloads files from Haiku's svn
232    if ! [ -f ${baseDir}/${1} ] ; then
233        echo "Fetching ${1} ..."
234        cd ${baseDir}
235        local baseURL=http://dev.haiku-os.org/export/
236        local revision=`uname -v | awk '{print $1}' | sed -e 's/r//'`
237        wget ${baseURL}${revision}/haiku/trunk/build/jam/${file} &> /dev/null
238    fi 
239}
240
241
242function GeneratePackageNames()
243{
244    # GeneratePackageNames
245    # Creates a file containing available package names
246    local outfile="${baseDir}/OptionalPackageNames"
247   
248    if ! [ -f ${outfile} ] ; then
249        echo "Generating a list of Package Names ..."
250       
251        touch ${outfile}
252        local regExp='/^if\ \[\ IsOptionalHaikuImagePackageAdded/p'
253        sed -n -e "$regExp" ${baseDir}/OptionalPackages > ${outfile}.temp
254        while read line ; do
255            local pkg=`echo ${line} | awk '{print $4}'`
256           
257            if ! IsPackageBlackListed ${pkg} ; then
258                echo ${pkg} >> ${outfile}
259            fi
260           
261        done < ${outfile}.temp
262        rm ${outfile}.temp
263    fi 
264   
265    # read list into array
266    local count=0
267    while read line ; do
268        availablePackages[$count]=$line
269        ((count++))
270    done < ${outfile}
271}
272
273
274function IsPackageBlackListed()
275{
276    # IsPackageBlackListed <pkg>
277    # Check if this package or any of its deps cannot be installed
278   
279    GetPackageDependencies ${1}
280   
281    local mustIgnore=1
282    for ignoreThisPackage in ${packageBlackList[*]} ; do
283        if [ ${1} == ${ignoreThisPackage} ] ; then
284            mustIgnore=0
285            break
286        else
287            for dep in ${tempDeps} ; do
288                if [ ${dep} == ${ignoreThisPackage} ] ; then
289                    mustIgnore=0
290                    break
291                fi
292            done
293        fi
294    done
295    return $mustIgnore
296}
297
298
299function IsPackageNameValid()
300{
301    # IsPackageNameValid <name>
302    if IsPackageBlackListed "$1" ; then 
303        echo "Due to limitations, the package \"$1\" cannot be installed."
304        exit 1
305    fi
306   
307    local packageExists=1
308    for name in ${availablePackages[*]} ; do
309        if [ "$1" == "$name" ] ; then
310            packageExists=0
311            packagesToInstall[0]=${1}
312            GetPackageDependencies "$1"
313                       
314            # move deps into packagesToInstall
315            if [ ${#tempDeps} -gt 0 ] ; then
316                for dep in ${tempDeps} ; do
317                    if [ ';' == "$dep" ] ; then
318                        break
319                    fi
320                    gPDi=${#packagesToInstall[@]}
321                    ((gPDi++))
322                    packagesToInstall[$gPDi]=${dep}
323                    GetPackageDependencies ${dep}
324                done
325            fi
326            echo "Packages to be installed:"
327            echo "    ${packagesToInstall[*]}"
328            return 0
329        fi
330    done
331   
332    if [ $packageExists -gt 0 ] ; then
333        echo "Package \"$1\" does not exist."
334        echo ''
335        exit 1
336    fi
337    return 1
338}
339
340
341function GetPackageDependencies()
342{
343    # getPackageDependecies <pkg>
344    # parse OptionalPackageDependencies for <pkg>'s dependencies
345    local regExp="^OptionalPackageDependencies\ ${1}"
346    local inputFile="${baseDir}/OptionalPackageDependencies"
347    sed -n -e "/${regExp}\ /p" ${inputFile} > ${tmpDir}/optpkg.temp
348    tempDeps=`sed -e "s/${regExp}\ :\ //" ${tmpDir}/optpkg.temp`
349    rm ${tmpDir}/optpkg.temp
350}
351
352
353function ContainsSubstring()
354{
355    # ContainsSubstring <stringToLookIn> <stringToLookFor>
356    local newString=${1/${2}/''}
357    if [ ${#1} -eq `expr ${#newString} + ${#2}` ] ; then
358        return 0
359    fi
360    return 1
361}
362
363function ReplaceComparators()
364{
365    # ReplaceComparators <line>
366    # One of the Jam-to-Bash conversion functions.
367   
368    # Preserve string comparators for TARGET_ARCH.
369    if ! ContainsSubstring "$line" 'TARGET_ARCH' ; then
370        line=${line//'>='/'-ge'}
371        line=${line//'<='/'-le'}
372        line=${line//'>'/'-gt'}
373        line=${line//'<'/'-lt'}
374        line=${line//'!='/'-ne'}
375    fi
376}
377
378
379function ConvertVariables()
380{
381    # ConvertVariables
382    # One of the Jam-to-Bash conversion functions.
383    # NOTE: jam's variables are normally '$(VARIABLE)'. \n
384    #       The issue is with '(' and ')', so let's replace them globally.
385   
386    if ContainsSubstring "$line" '$(' ; then
387        line=${line//'('/'{'}
388        line=${line//')'/'}'}
389    fi
390}
391
392
393function ConvertVariableDeclarationLines()
394{
395    # ConvertVariableDeclarationLines <regex> <variable>
396    # One of the Jam-to-Bash conversion functions.
397    # Jam lines that define variables need to be parsed differently.
398    eval local input='$'"$2"
399    local regex="$1"
400    local _outvar="$2"
401   
402    input=${input/\ =\ /=}
403    input=${input/\;/''}
404    input=${input//\(/'{'}
405    input=${input//\)/'}'}
406   
407    eval $_outvar="'$input'"
408}
409
410
411
412function ConvertIfStatements()
413{
414    # ConvertIfStatements <line>   
415    # One of the Jam-to-Bash conversion functions.
416    line=${line//'} else {'/'else '}
417    line=${line//'} else if '/'elif '}
418    if ContainsSubstring "$line" "if " ; then
419        if ! ContainsSubstring "$line" "if [" ; then
420            line=${line/'if '/'if [ '}
421        fi
422       
423        if ContainsSubstring "$line" '] {' ; then
424            line=${line/'{'/' ; then'}
425        elif ContainsSubstring "$line" '{' ; then
426            line=${line/'{'/' ] ; then'}
427        fi
428       
429        for compound in '&&' '||' ; do
430            if ContainsSubstring "$line" "$compound" ; then
431                line=${line/"$compound"/"] $compound ["}
432            fi
433        done
434    fi
435    # Assume all remaining closing braces are part of if statements
436    line=${line/'}'/'fi'}
437}
438
439
440function ConvertJamToBash()
441{
442    # ConvertJamToBash <input file>
443    # The main Jam-to-Bash conversion function.
444    local inputFile=$1
445    declare -a generatedBash
446    countGenBashLine=0
447   
448    # Parse out some variable declarations
449    local regExp='/^HAIKU_OPENSSL_PACKAGE/p'
450    sslPkgLine=`sed -n -e "$regExp" ${baseDir}/OptionalBuildFeatures`
451    ConvertVariableDeclarationLines "$regExp" 'sslPkgLine'
452       
453    local regExp='/^HAIKU_OPENSSL_URL/p'
454    sslUrlLine=`sed -n -e "$regExp" ${baseDir}/OptionalBuildFeatures`
455    ConvertVariableDeclarationLines "$regExp" 'sslUrlLine'
456   
457    local regExp='/^local\ baseURL/p'
458    urlLine=`sed -n -e "$regExp" ${baseDir}/OptionalPackages`
459    urlLine=${urlLine/local\ /''}
460    ConvertVariableDeclarationLines "$regExp" 'urlLine'
461   
462    # Convert the easy bits.
463    while read line ; do
464        line=${line/'Echo'/'echo'}
465       
466        ConvertIfStatements "$line"
467        ConvertVariables "$line"
468        ReplaceComparators "$line"
469       
470        line=${line/"IsOptionalHaikuImagePackageAdded"/'"SomeText" !='}
471        generatedBash[$countGenBashLine]=${line}
472        ((countGenBashLine++))
473    done < ${tmpDir}/optpkg.jam
474   
475    # output stage 1 generated code
476    local i=0
477    while [ $i -lt $countGenBashLine ] ; do
478        echo ${generatedBash[$i]} >> ${tmpDir}/optpkg.stage1
479        ((i++))
480    done
481   
482    # This converts multi-line jam statements into a single line.
483    # --- Start awk ---
484    awk '
485        /InstallOptionalHaikuImagePackage/,/\;/{
486            ruleFound=1;
487            if($0~/\;/) ORS="\n";
488            else ORS=" "; print
489        }
490        /AddSymlinkToHaikuImage/,/\;/{
491            ruleFound=1;
492            if($0~/\;/) ORS="\n";
493            else ORS=" "; print
494        }
495        /AddUserToHaikuImage/,/\;/{
496            ruleFound=1;
497            if($0~/\;/) ORS="\n";
498            else ORS=" "; print
499        }
500        /Exit/,/\;/{
501            ruleFound=1;
502            if($0~/\;/) ORS="\n";
503            else ORS=" "; print
504        }       
505        {
506            if($1!='InstallOptionalHaikuImagePackage' && ruleFound!=1 && $1!="\;")
507            print $0
508        }
509        { ruleFound=0; }
510        ' ${tmpDir}/optpkg.stage1 > ${tmpDir}/optpkg.stage2 2>/dev/null
511    # --- End awk ---
512    rm ${tmpDir}/optpkg.stage1
513}
514
515
516function ListPackages()
517{
518    # ListPackages
519    echo "Available Optional Packages:"
520    for package in ${availablePackages[*]} ; do
521        echo ${package}
522    done
523}
524
525
526function AddPackage()
527{
528    # AddPackage <name>
529    if IsPackageNameValid $1  ; then
530        for package in ${packagesToInstall[*]} ; do
531            local regExp="if\ \[\ IsOptionalHaikuImagePackageAdded\ ${package}"
532            local inputFile="${baseDir}/OptionalPackages"
533            sed -n "/^$regExp/,/^\}/p" ${inputFile} >> ${tmpDir}/optpkg.jam
534        done
535        ConvertJamToBash "${tmpDir}/optpkg.jam"
536        rm "${tmpDir}/optpkg.jam"
537    CreateInstallerScript
538    sh ${tmpDir}/install-optpkg.sh
539    rm ${tmpDir}/install-optpkg.sh
540    fi
541}
542
543
544function DisplayUsage()
545{
546    echo ''
547    echo 'Disclaimer:'
548    echo '  This is a temporary solution for installing Optional Packages.'
549    echo '  In time, there will be an official package manager.'
550    echo "  See these URL's for info on the in-development package manager."
551    echo '    http://dev.haiku-os.org/wiki/PackageManagerIdeas'
552    echo '    http://dev.haiku-os.org/wiki/PackageFormat'
553    echo ''
554    echo "Usage: $0 [-l] [-a]"
555    echo "-l    List installable packages"
556    echo "-a    Add a package and all of its dependencies"
557    echo ''
558}
559
560
561function DetectSystemConfiguration()
562{
563    # Determine which GCC we're running and if we're a hybrid
564    if [ -d "$libDir"/gcc4 ] ; then
565        HAIKU_GCC_VERSION[1]=2
566        isHybridBuild=1
567    elif [ -d "$libDir"/gcc2 ] ; then
568        HAIKU_GCC_VERSION[1]=4
569        isHybridBuild=1
570    elif [ -f "$libDir"/libsupc++.so ] ; then
571        HAIKU_GCC_VERSION[1]=4
572        isHybridBuild=0
573    else
574        HAIKU_GCC_VERSION[1]=2
575        isHybridBuild=0
576    fi
577
578    # Determine the Architecture.
579    if [ `uname -m` == "BePC" ] ; then
580        TARGET_ARCH='x86'
581    fi
582}
583
584function Init()
585{
586   
587    # Set up some directory paths
588    baseDir=`finddir B_COMMON_DATA_DIRECTORY`/optional-packages
589    tmpDir=`finddir B_COMMON_TEMP_DIRECTORY`
590    libDir=`finddir B_SYSTEM_LIB_DIRECTORY`
591
592    # Make sure these files are empty.
593    echo "" > ${tmpDir}/optpkg.jam
594    echo "" > ${tmpDir}/optpkg.stage1
595   
596   
597    if ! [ -d ${baseDir} ] ; then
598        mkdir -p ${baseDir}
599    fi
600   
601    # Retreive the necessary jam files from svn.
602    local buildFiles=( 'OptionalPackages' 'OptionalPackageDependencies' \
603        'OptionalBuildFeatures')
604    for file in ${buildFiles[*]} ; do
605        GetBuildFile ${file}
606    done
607
608    DetectSystemConfiguration
609    GeneratePackageNames
610}
611
612
613# If no arguments were passed to the script, display its usage and exit.
614if [ "$#" -lt 1 ] ; then
615    DisplayUsage
616    exit 0
617else
618    Init
619fi
620
621# Parse the arguments given to the script.
622while getopts "a:lh" opt; do
623    case $opt in
624        a)
625            AddPackage $OPTARG
626            exit 0
627            ;;
628        h)
629            DisplayUsage
630            exit 0
631            ;;
632        l)
633            ListPackages
634            exit 0
635            ;;
636        \?)
637            echo "Invalid option: -$OPTARG" >&2
638            exit 1
639            ;;
640        :)
641            echo "Option -$OPTARG requires an argument." >&2
642            exit 1
643            ;;
644    esac
645done