From e7660d9ccd5330bca286989f12a62638a3e09155 Mon Sep 17 00:00:00 2001 From: "Lucas C. Villa Real" Date: Mon, 2 Jan 2017 19:24:01 -0200 Subject: Rename main script. --- bin/InstallPackage-RPM | 508 ------------------------------------------------ bin/ThirdPartyInstaller | 508 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 508 insertions(+), 508 deletions(-) delete mode 100755 bin/InstallPackage-RPM create mode 100755 bin/ThirdPartyInstaller diff --git a/bin/InstallPackage-RPM b/bin/InstallPackage-RPM deleted file mode 100755 index 099f670..0000000 --- a/bin/InstallPackage-RPM +++ /dev/null @@ -1,508 +0,0 @@ -#!/bin/bash - -source ScriptFunctions -Import File -Import GoboLinux -Import Log -Import OptionParser - -### Options ################################################################### - -scriptDescription="Install RPM and DEB packages on GoboLinux." -scriptCredits="Copyright (C) Lucas C. Villa Real, 2016,2017 - Released under the GNU GPL." -helpOnNoArguments=yes -scriptUsage=" [file.rpm]" -scriptExample="xispita-2.0.3-1.x86_64.rpm" - -Add_Option_Entry "n" "app-name" "Override program name" -Add_Option_Entry "e" "version-number" "Override program version number" -Add_Option_Entry "l" "symlink" "If symlinks should be created and wether they should be forced on conflicts." "yes" "yes no force" -Add_Option_Boolean "W" "no-web" "Do not search the web to resolve dependencies." -Parse_Options "$@" - -### Functions ################################################################# - -function fetch_package() { - local inputfile="$1" - local filename=$(basename "$inputfile") - if Is_URL "$inputfile" - then - if wget --help | grep -q "no-check-certificate" - then wget_cmd="wget --no-check-certificate" - else wget_cmd="wget" - fi - Quiet ${wget_cmd} -c "${inputfile}" -O "${goboTemp}/${filename}" || Die "Error downloading package." - echo "${goboTemp}/${filename}" - else - echo "$inputfile" - fi -} - -function uncompress_package() { - local inputfile="$1" - thirdparty_uncompress "$inputfile" -} - -function determine_flattening_level() { - local rootdirs="bin$\|sbin$\|lib$\|lib64$\|libexec$\|include$\|share$" - local filenames=$(for i in "${inputfiles[@]}"; do thirdparty_filenames $i; done) - - Log_Verbose "$filenames" - if echo "$filenames" | grep -q "/opt/$rootdirs" - then - # 1-level /opt hierarchy - echo "1" - elif echo "$filenames" | grep -q "/opt/[^/]*/$rootdirs" - then - # 2-level /opt hierarchy - echo "2" - elif echo "$filenames" | grep -q "/opt" - then - # Get the longest common prefix among the $filenames list that starts - # with "/opt" and then determine how many path levels we have there. - local commonprefix=$(echo "$filenames" | grep "/opt" | sed -e 'N;s/^\(.*\).*\n\1.*$/\1\n\1/;D') - local numslashes=$(echo "$commonprefix" | grep -o "/" | wc -l) - let numslashes=numslashes-1 - if [ "$numslashes" = 1 ] - then - Log_Verbose "Package seems to have a 1-level /opt structure" - echo "1" - elif [ "$numslashes" = 2 ] - then - Log_Verbose "Package seems to have a 2-level /opt structure" - echo "2" - else - Log_Error "Could not determine this package's /opt structure, assuming 2-level" - echo "2" - fi - fi -} - -function flatten_package() { - local inputfile="$1" - - Log_Normal "Flattening directory structure." - - function create_opt_links() { - local vendordir="$1" - local unmanagedopt="Resources/Unmanaged/opt" - for pkgdir in $(basename --multiple ./opt/$vendordir/*) - do - mkdir ${verbose} -p "${unmanagedopt}/$vendordir/$pkgdir" - if [ -h "/opt/$vendordir/$pkgdir" ] - then - Log_Error "Too many programs are attempting to populate /opt/$vendordir/$pkgdir" - Log_Error "This feature is not supported by InstallPackage-RPM at this time." - continue - fi - for optfile in $(basename --multiple ./opt/$vendordir/$pkgdir/*) - do - if [ ! -e "${unmanagedopt}/$vendordir/$pkgdir/$optfile" ] - then - ln ${verbose} -fs $target/$optfile "${unmanagedopt}/$vendordir/$pkgdir/$optfile" - fi - done - done - } - - function flatten_opt_1_level() { - # Flatten 1-level dir: opt/pkgname/{bin,sbin,...} - cp ${verbose} -a ./opt/*/* . - create_opt_links "" - realpath ./opt/* | sed "s,$(realpath $PWD),,g" >> Resources/UnmanagedFiles - } - - function flatten_opt_2_levels() { - # Flatten 2-levels dir: opt/vendorname/pkgname/{bin,sbin,...} - cp ${verbose} -a ./opt/*/*/* . - for vendordir in $(basename --multiple ./opt/*) - do - create_opt_links "$vendordir" - done - realpath ./opt/*/* | sed "s,$(realpath $PWD),,g" >> Resources/UnmanagedFiles - } - - if [ -d "./usr" ] - then - cp ${verbose} -a ./usr/* . - rm -rf -- ./usr - fi - if [ -d "./etc" ] - then - mkdir -p Resources/Defaults/Settings - mv ${verbose} ./etc/* Resources/Defaults/Settings - rm -rf -- ./etc - fi - if [ -d "./lib64" ] - then - Quiet rmdir lib - if [ ! -d "./lib" ] - then mv ./lib64 lib - else cp -a ./lib64/* ./lib/ - fi - rm -rf ./lib64 - fi - if [ -d "./var" ] - then - mkdir -p Resources/Unmanaged/$goboVariable - find ./var | sed "s,./var,$goboVariable,g" >> Resources/UnmanagedFiles - mv ${verbose} ./var/* Resources/Unmanaged/$goboVariable - rm -rf -- ./var - fi - if [ -d "./opt" ] - then - # Prevent the creation of backlinks to directories that would be pruned later on - find share -type d | xargs rmdir -p --ignore-fail-on-non-empty - rmdir * 2> /dev/null - mkdir -p Resources/Unmanaged/opt - if [ "$flatteninglevel" = "1" ] - then - Log_Verbose "Flattening 1-level /opt directory" - flatten_opt_1_level - elif [ "$flatteninglevel" = "2" ] - then - Log_Verbose "Flattening 2-level /opt directory" - flatten_opt_2_levels - fi - rm -rf -- ./opt - fi - if [ -e Resources/UnmanagedFiles ] - then - # If multiple RPM files are being merged, then ensure we have no dups - cat Resources/UnmanagedFiles | sort -n | uniq > x && mv x Resources/UnmanagedFiles - fi - rmdir * 2> /dev/null -} - -function is_inputfile() { - local dependency="$1" - local pkgname=$(echo "$dependency" | cut -d'(' -f1 | awk {'print $1'}) - printf "%s\n" "${inputnames[@]}" | grep -q "^${pkgname}$" && return 0 - return 1 -} - -function populate_dependencies() { - local inputfile="$1" - thirdparty_dependencies "$inputfile" | while read dependency - do - if echo "$dependency" | grep -q "^/" - then - depinfo=$(take_dependency_from_path "$inputfile" "$dependency" "") - if [ "$depinfo" ] - then echo "$depinfo" - else echo "# Unresolved path-based dependency: $dependency" - fi - elif echo "$dependency" | grep -q "^lib.*\.so*\|.*\.so\.*" - then - libname=$(echo "$dependency" | cut -d'(' -f1) - wantedsymbol=$(echo "$dependency" | cut -d'(' -f2 | cut -d')' -f1) - depinfo=$(take_dependency_from_path "$inputfile" "$goboLibraries/$libname" "$wantedsymbol") - if [ "$depinfo" ] - then echo "$depinfo" - else echo "# Unresolved path-based library dependency: $dependency" - fi - elif is_basic_symbol "$dependency" - then - Log_Verbose "Skipping basic symbol: $dependency" - elif is_inputfile "$dependency" - then - Log_Verbose "Skipping dependency passed as input file: $dependency" - else - depinfo=$(lookup_pkgname "$dependency") - if [ "$depinfo" ] - then echo "$depinfo" - else echo "# Unresolved dependency: $dependency" - fi - fi - done -} - -function populate_resources() { - local inputfile="$1" - local arch=$(thirdparty_arch "$inputfile") - local description=$(thirdparty_description "$inputfile") - local release=$(thirdparty_release "$inputfile") - local distro=$(thirdparty_distribution "$inputfile") - - Log_Normal "Populating Resources." - mkdir -p Resources - - if [ "$arch" ] - then echo "$arch" > Resources/Architecture - else echo "$(uname -m)" > Resources/Architecture - fi - echo "$release" > Resources/Revision - - # Note that we never truncate neither Resources/PackageSource nor - # Resources/Description or Resources/Dependencies. This is to - # enable the installation of multiple RPM/DEB files under the same - # /Programs entry while keeing metadata of all the original files - # around. - - [ -e Resources/PackageSource ] && echo >> Resources/PackageSource - echo "[File] $(basename $inputfile)" >> Resources/PackageSource - echo "[Distribution] $distro" >> Resources/PackageSource - - if [ "$description" ] - then - [ -e Resources/Description ] && echo "" >> Resources/Description - echo "[Name] $(thirdparty_name $inputfile)" >> Resources/Description - echo "[Summary] $(thirdparty_summary $inputfile)" >> Resources/Description - echo "[License] $(thirdparty_license $inputfile)" >> Resources/Description - echo "[Description] $(thirdparty_description $inputfile)" >> Resources/Description - echo "[Homepage] $(thirdparty_url $inputfile)" >> Resources/Description - fi - - Log_Normal "Processing dependencies." - alldeps=$(mktemp ThirdPartyInstaller.XXXXXXXXXX) - populate_dependencies "$inputfile" >> Resources/Dependencies - cat Resources/Dependencies | sort -n | uniq > "$alldeps" - cat "$alldeps" > Resources/Dependencies - rm -f -- "$alldeps" -} - -function lookup_symbol() { - local depname="$1" - local testversion="$2" - local arch="$3" - local symbol="$4" - local testarch=$(cat "$goboPrograms/$depname/$testversion/Resources/Architecture" 2> /dev/null) - - if [ "$testarch" ] && [ "$testarch" = "$arch" ] - then - Log_Verbose "Looking for symbol $symbol on $goboPrograms/$depname/$testversion/$path" - if nm "$goboPrograms/$depname/$testversion/$path" 2> /dev/null | grep --max-count=1 -q "$symbol" - then - Log_Verbose "Match: $depname $testversion" - echo "$depname $testversion" - return 0 - fi - fi - return 1 -} - -function take_dependency_from_path() { - # TODO since $2 may come in the form '/usr/lib/libfoo >= version', we have to pick the - # path from the first part of the string. Right now we're using awk to get it, but that - # will not work if the dependency path contains spaces. - - local inputfile="$1" - local originalpath=$(echo "$2" | awk '{print $1}') - local path=$(echo "$originalpath" | sed 's,/usr,,g') - local symbol="$3" - local fullpath="$(readlink -f $path)" - local arch=$(thirdparty_arch "$inputfile") - local distro=$(thirdparty_distribution "$inputfile") - - local depname= - local depversion= - - if echo "$fullpath" | grep -q "^${goboPrograms}" - then - # If given, we search for the presence of @symbol on the given target file. - # We iterate over different installations of the same program looking for - # that symbol. If none of the installations have it, we fallback to printing - # the dependency currently linked on /System/Index. - # - # Note that when iterating over installed programs we skip those entries whose - # Resources/Architecture do not match the output of $(rpminfo --arch). - - depname=$(echo "$fullpath" | cut -d/ -f3) - depversion=$(echo "$fullpath" | cut -d/ -f4) - if [ "$symbol" ] - then - for testversion in $(ls $goboPrograms/$depname/ | grep -v "Settings\|Variable\|Current") - do - lookup_symbol "$depname" "$testversion" "$arch" "$symbol" && return 0 - done - fi - - Log_Verbose "Fallback: $depname $depversion" - echo "$depname $depversion" - else - # We have a path, but we don't have a link to that file under /System/Index. - # Our first attempt is to search over the list of installed programs anyhow, - # because some programs may not be currently activated. - - for fullpath in $(ls $goboPrograms/*/*/$path 2> /dev/null | grep -v "Current") - do - depname=$(echo "$fullpath" | cut -d/ -f3) - testversion=$(echo "$fullpath" | cut -d/ -f4) - [ -z "$depversion" ] && depversion="$testversion" - Log_Verbose "Looking for symbol on candidate file $candidate ($depname, $testversion)" - lookup_symbol "$depname" "$testversion" "$arch" "$symbol" && return 0 - done - - # We don't have a match. If we have a file name that satisfies the path but - # that doesn't contain the requested symbol, we simply return that path. - - if [ "$depname" ] && [ "$depversion" ] - then - echo "$depname $depversion" - return 0 - fi - - # We don't have a matching filename under /System/Index nor under /Programs/*/*. - # What we do now is to query remote RPM/DEB databases to find which package hosts the - # dependency file. - - if ! Boolean "no-web" - then - Log_Normal "Searching the remote $(thirdparty_backend) database for the package hosting $originalpath" - depname=$(third_party_search_remotedb "$originalpath" "$arch" "$distro") - if [ "$depname" ] - then - # TODO: we could now lookup the GoboLinux recipe store to find whether we - # have it or not - echo "$(GuessProgramCase $depname)" - return 0 - fi - fi - fi -} - -function lookup_pkgname() { - local dependency="$1" - local pkgname=$(echo "$dependency" | cut -d'(' -f1) - - # GoboLinux doesn't have "devel" packages like most mainstream distros do - local goboname=$(echo "$pkgname" | sed 's,-devel$,,g') - - # Do we have a GoboLinux package installed with a matching name? - for testname in $(ls $goboPrograms/) - do - # Case-insensitive comparison (requires Bash 4) - if [ "${testname,,}" = "${pkgname,,}" ] || [ "${testname,,}" = "${goboname,,}" ] - then - echo "$testname" && return 0 - fi - done - - # Query the GoboLinux recipe store. - if ! Boolean "no-web" - then - - local recipeurl=$(FindPackage -t recipe "${pkgname}" || FindPackage -t recipe "${goboname}") - if [ "$recipeurl" ] - then - # TODO we're potentially discarding the wanted version(s) of the given dep - echo "$(basename $recipeurl | sed 's,\(.*\)--.*--.*,\1,g')" && return 0 - fi - fi - - # TODO Query the remote RPM database for $pkgname - - # TODO Query the remote DEB database for $pkgname - - return 1 -} - -function is_basic_symbol() { - local dependency="$1" - echo "$dependency" | grep -q "^rtld(" && return 0 - return 1 -} - -function deduce_program_name() { - local inputfile="$1" - local name=$(thirdparty_name "$inputfile") - echo $name -} - -function prepare_program_entry() { - local inputfile="$1" - - if [ ${#inputfiles[@]} -gt 1 ] && Is_Entry "app-name" && [ ! -z "$programname" ] - then - # We have already prepared this program's entry on /Programs - return - fi - if Is_Entry "app-name" - then programname=$(Entry "app-name") - else programname="$(deduce_program_name $inputfile)" - fi - if Is_Entry "version-number" - then programversion=$(Entry "version-number") - else programversion=$(printf "%s_%s" $(thirdparty_version "$inputfile") $(thirdparty_release "$inputfile")) - fi - - # Prepare /Programs tree and update program name (PrepareProgram may have changed its case) - PrepareProgram -t "$programname" "$programversion" - programname=$(ls $goboPrograms/ | grep -i "^${programname}$") - target="$goboPrograms/$programname/$programversion" -} - -### Operation ################################################################# - -Is_Writable "${goboPrograms}" || Verify_Superuser - -if Boolean "verbose" -then verbose="--verbose" -else verbose= -fi - -# The inputfiles array holds the full path of all RPM/DEB input files -# The inputnames array holds the package name of all RPM/DEB input files -inputfiles=() -inputnames=() -rpmcount=0 -debcount=0 -eval `Args_To_Array inputfiles_` -for entry in "${inputfiles_[@]}" -do - inputfiles+=( "$(readlink -f ${entry} || echo ${entry})" ) - inputnames+=( "$(rpminfo --name $entry)" ) - echo "$entry" | grep -qi "\.deb" && let debcount=debcount+1 - echo "$entry" | grep -qi "\.rpm" && let rpmcount=rpmcount+1 -done - -# Sanity checks, then import the backend to handle the package format -if [ $rpmcount -gt 0 ] && [ $debcount -gt 0 ] -then - Die "Error: cannot handle both RPM and DEB files in a single shot." -elif [ $rpmcount -gt 0 ] -then - Import RPM -elif [ $debcount -gt 0 ] -then - Import DEB -fi - -# These will be set by prepare_program_entry() -unset programname -unset programversion -unset target - -# Determine the flattening level of /opt. In other words, whether -# we have something like: -# /opt/pkgname/{bin,sbin...} (1-level), or -# /opt/vendorname/pkgname/{bin,sbin...} (2-level) - -flatteninglevel=$(determine_flattening_level) - -# Installation pipeline -for entry in "${inputfiles[@]}" -do - Log_Normal "Processing $(basename $entry)" - inputfile=$(fetch_package "$entry") - prepare_program_entry "$inputfile" - Quiet pushd "$target" || Die "Could not enter $target" - - uncompress_package "$inputfile" - flatten_package "$inputfile" - populate_resources "$inputfile" - - Quiet popd - Is_URL "$entry" && rm -f -- "$inputfile" -done - -# Symlinking -if [ $(Entry "symlink") = "no" ] -then - Log_Normal "Done." - exit 0 -fi - -[ -d "$target/Resources/Defaults/Settings" ] && UpdateSettings "$programname" "$programversion" -SymlinkProgram "$programname" "$programversion" -Log_Normal "Done." diff --git a/bin/ThirdPartyInstaller b/bin/ThirdPartyInstaller new file mode 100755 index 0000000..131a31e --- /dev/null +++ b/bin/ThirdPartyInstaller @@ -0,0 +1,508 @@ +#!/bin/bash + +source ScriptFunctions +Import File +Import GoboLinux +Import Log +Import OptionParser + +### Options ################################################################### + +scriptDescription="Install RPM and DEB packages on GoboLinux." +scriptCredits="Copyright (C) Lucas C. Villa Real, 2016,2017 - Released under the GNU GPL." +helpOnNoArguments=yes +scriptUsage=" [file.rpm]" +scriptExample="xispita-2.0.3-1.x86_64.rpm" + +Add_Option_Entry "n" "app-name" "Override program name" +Add_Option_Entry "e" "version-number" "Override program version number" +Add_Option_Entry "l" "symlink" "If symlinks should be created and wether they should be forced on conflicts." "yes" "yes no force" +Add_Option_Boolean "W" "no-web" "Do not search the web to resolve dependencies." +Parse_Options "$@" + +### Functions ################################################################# + +function fetch_package() { + local inputfile="$1" + local filename=$(basename "$inputfile") + if Is_URL "$inputfile" + then + if wget --help | grep -q "no-check-certificate" + then wget_cmd="wget --no-check-certificate" + else wget_cmd="wget" + fi + Quiet ${wget_cmd} -c "${inputfile}" -O "${goboTemp}/${filename}" || Die "Error downloading package." + echo "${goboTemp}/${filename}" + else + echo "$inputfile" + fi +} + +function uncompress_package() { + local inputfile="$1" + thirdparty_uncompress "$inputfile" +} + +function determine_flattening_level() { + local rootdirs="bin$\|sbin$\|lib$\|lib64$\|libexec$\|include$\|share$" + local filenames=$(for i in "${inputfiles[@]}"; do thirdparty_filenames $i; done) + + Log_Verbose "$filenames" + if echo "$filenames" | grep -q "/opt/$rootdirs" + then + # 1-level /opt hierarchy + echo "1" + elif echo "$filenames" | grep -q "/opt/[^/]*/$rootdirs" + then + # 2-level /opt hierarchy + echo "2" + elif echo "$filenames" | grep -q "/opt" + then + # Get the longest common prefix among the $filenames list that starts + # with "/opt" and then determine how many path levels we have there. + local commonprefix=$(echo "$filenames" | grep "/opt" | sed -e 'N;s/^\(.*\).*\n\1.*$/\1\n\1/;D') + local numslashes=$(echo "$commonprefix" | grep -o "/" | wc -l) + let numslashes=numslashes-1 + if [ "$numslashes" = 1 ] + then + Log_Verbose "Package seems to have a 1-level /opt structure" + echo "1" + elif [ "$numslashes" = 2 ] + then + Log_Verbose "Package seems to have a 2-level /opt structure" + echo "2" + else + Log_Error "Could not determine this package's /opt structure, assuming 2-level" + echo "2" + fi + fi +} + +function flatten_package() { + local inputfile="$1" + + Log_Normal "Flattening directory structure." + + function create_opt_links() { + local vendordir="$1" + local unmanagedopt="Resources/Unmanaged/opt" + for pkgdir in $(basename --multiple ./opt/$vendordir/*) + do + mkdir ${verbose} -p "${unmanagedopt}/$vendordir/$pkgdir" + if [ -h "/opt/$vendordir/$pkgdir" ] + then + Log_Error "Too many programs are attempting to populate /opt/$vendordir/$pkgdir" + Log_Error "This feature is not supported by ThirdPartyInstaller at this time." + continue + fi + for optfile in $(basename --multiple ./opt/$vendordir/$pkgdir/*) + do + if [ ! -e "${unmanagedopt}/$vendordir/$pkgdir/$optfile" ] + then + ln ${verbose} -fs $target/$optfile "${unmanagedopt}/$vendordir/$pkgdir/$optfile" + fi + done + done + } + + function flatten_opt_1_level() { + # Flatten 1-level dir: opt/pkgname/{bin,sbin,...} + cp ${verbose} -a ./opt/*/* . + create_opt_links "" + realpath ./opt/* | sed "s,$(realpath $PWD),,g" >> Resources/UnmanagedFiles + } + + function flatten_opt_2_levels() { + # Flatten 2-levels dir: opt/vendorname/pkgname/{bin,sbin,...} + cp ${verbose} -a ./opt/*/*/* . + for vendordir in $(basename --multiple ./opt/*) + do + create_opt_links "$vendordir" + done + realpath ./opt/*/* | sed "s,$(realpath $PWD),,g" >> Resources/UnmanagedFiles + } + + if [ -d "./usr" ] + then + cp ${verbose} -a ./usr/* . + rm -rf -- ./usr + fi + if [ -d "./etc" ] + then + mkdir -p Resources/Defaults/Settings + mv ${verbose} ./etc/* Resources/Defaults/Settings + rm -rf -- ./etc + fi + if [ -d "./lib64" ] + then + Quiet rmdir lib + if [ ! -d "./lib" ] + then mv ./lib64 lib + else cp -a ./lib64/* ./lib/ + fi + rm -rf ./lib64 + fi + if [ -d "./var" ] + then + mkdir -p Resources/Unmanaged/$goboVariable + find ./var | sed "s,./var,$goboVariable,g" >> Resources/UnmanagedFiles + mv ${verbose} ./var/* Resources/Unmanaged/$goboVariable + rm -rf -- ./var + fi + if [ -d "./opt" ] + then + # Prevent the creation of backlinks to directories that would be pruned later on + find share -type d | xargs rmdir -p --ignore-fail-on-non-empty + rmdir * 2> /dev/null + mkdir -p Resources/Unmanaged/opt + if [ "$flatteninglevel" = "1" ] + then + Log_Verbose "Flattening 1-level /opt directory" + flatten_opt_1_level + elif [ "$flatteninglevel" = "2" ] + then + Log_Verbose "Flattening 2-level /opt directory" + flatten_opt_2_levels + fi + rm -rf -- ./opt + fi + if [ -e Resources/UnmanagedFiles ] + then + # If multiple RPM/DEB files are being merged, then ensure we have no dups + cat Resources/UnmanagedFiles | sort -n | uniq > x && mv x Resources/UnmanagedFiles + fi + rmdir * 2> /dev/null +} + +function is_inputfile() { + local dependency="$1" + local pkgname=$(echo "$dependency" | cut -d'(' -f1 | awk {'print $1'}) + printf "%s\n" "${inputnames[@]}" | grep -q "^${pkgname}$" && return 0 + return 1 +} + +function populate_dependencies() { + local inputfile="$1" + thirdparty_dependencies "$inputfile" | while read dependency + do + if echo "$dependency" | grep -q "^/" + then + depinfo=$(take_dependency_from_path "$inputfile" "$dependency" "") + if [ "$depinfo" ] + then echo "$depinfo" + else echo "# Unresolved path-based dependency: $dependency" + fi + elif echo "$dependency" | grep -q "^lib.*\.so*\|.*\.so\.*" + then + libname=$(echo "$dependency" | cut -d'(' -f1) + wantedsymbol=$(echo "$dependency" | cut -d'(' -f2 | cut -d')' -f1) + depinfo=$(take_dependency_from_path "$inputfile" "$goboLibraries/$libname" "$wantedsymbol") + if [ "$depinfo" ] + then echo "$depinfo" + else echo "# Unresolved path-based library dependency: $dependency" + fi + elif is_basic_symbol "$dependency" + then + Log_Verbose "Skipping basic symbol: $dependency" + elif is_inputfile "$dependency" + then + Log_Verbose "Skipping dependency passed as input file: $dependency" + else + depinfo=$(lookup_pkgname "$dependency") + if [ "$depinfo" ] + then echo "$depinfo" + else echo "# Unresolved dependency: $dependency" + fi + fi + done +} + +function populate_resources() { + local inputfile="$1" + local arch=$(thirdparty_arch "$inputfile") + local description=$(thirdparty_description "$inputfile") + local release=$(thirdparty_release "$inputfile") + local distro=$(thirdparty_distribution "$inputfile") + + Log_Normal "Populating Resources." + mkdir -p Resources + + if [ "$arch" ] + then echo "$arch" > Resources/Architecture + else echo "$(uname -m)" > Resources/Architecture + fi + echo "$release" > Resources/Revision + + # Note that we never truncate neither Resources/PackageSource nor + # Resources/Description or Resources/Dependencies. This is to + # enable the installation of multiple RPM/DEB files under the same + # /Programs entry while keeing metadata of all the original files + # around. + + [ -e Resources/PackageSource ] && echo >> Resources/PackageSource + echo "[File] $(basename $inputfile)" >> Resources/PackageSource + echo "[Distribution] $distro" >> Resources/PackageSource + + if [ "$description" ] + then + [ -e Resources/Description ] && echo "" >> Resources/Description + echo "[Name] $(thirdparty_name $inputfile)" >> Resources/Description + echo "[Summary] $(thirdparty_summary $inputfile)" >> Resources/Description + echo "[License] $(thirdparty_license $inputfile)" >> Resources/Description + echo "[Description] $(thirdparty_description $inputfile)" >> Resources/Description + echo "[Homepage] $(thirdparty_url $inputfile)" >> Resources/Description + fi + + Log_Normal "Processing dependencies." + alldeps=$(mktemp ThirdPartyInstaller.XXXXXXXXXX) + populate_dependencies "$inputfile" >> Resources/Dependencies + cat Resources/Dependencies | sort -n | uniq > "$alldeps" + cat "$alldeps" > Resources/Dependencies + rm -f -- "$alldeps" +} + +function lookup_symbol() { + local depname="$1" + local testversion="$2" + local arch="$3" + local symbol="$4" + local testarch=$(cat "$goboPrograms/$depname/$testversion/Resources/Architecture" 2> /dev/null) + + if [ "$testarch" ] && [ "$testarch" = "$arch" ] + then + Log_Verbose "Looking for symbol $symbol on $goboPrograms/$depname/$testversion/$path" + if nm "$goboPrograms/$depname/$testversion/$path" 2> /dev/null | grep --max-count=1 -q "$symbol" + then + Log_Verbose "Match: $depname $testversion" + echo "$depname $testversion" + return 0 + fi + fi + return 1 +} + +function take_dependency_from_path() { + # TODO since $2 may come in the form '/usr/lib/libfoo >= version', we have to pick the + # path from the first part of the string. Right now we're using awk to get it, but that + # will not work if the dependency path contains spaces. + + local inputfile="$1" + local originalpath=$(echo "$2" | awk '{print $1}') + local path=$(echo "$originalpath" | sed 's,/usr,,g') + local symbol="$3" + local fullpath="$(readlink -f $path)" + local arch=$(thirdparty_arch "$inputfile") + local distro=$(thirdparty_distribution "$inputfile") + + local depname= + local depversion= + + if echo "$fullpath" | grep -q "^${goboPrograms}" + then + # If given, we search for the presence of @symbol on the given target file. + # We iterate over different installations of the same program looking for + # that symbol. If none of the installations have it, we fallback to printing + # the dependency currently linked on /System/Index. + # + # Note that when iterating over installed programs we skip those entries whose + # Resources/Architecture do not match the output of $(rpminfo --arch). + + depname=$(echo "$fullpath" | cut -d/ -f3) + depversion=$(echo "$fullpath" | cut -d/ -f4) + if [ "$symbol" ] + then + for testversion in $(ls $goboPrograms/$depname/ | grep -v "Settings\|Variable\|Current") + do + lookup_symbol "$depname" "$testversion" "$arch" "$symbol" && return 0 + done + fi + + Log_Verbose "Fallback: $depname $depversion" + echo "$depname $depversion" + else + # We have a path, but we don't have a link to that file under /System/Index. + # Our first attempt is to search over the list of installed programs anyhow, + # because some programs may not be currently activated. + + for fullpath in $(ls $goboPrograms/*/*/$path 2> /dev/null | grep -v "Current") + do + depname=$(echo "$fullpath" | cut -d/ -f3) + testversion=$(echo "$fullpath" | cut -d/ -f4) + [ -z "$depversion" ] && depversion="$testversion" + Log_Verbose "Looking for symbol on candidate file $candidate ($depname, $testversion)" + lookup_symbol "$depname" "$testversion" "$arch" "$symbol" && return 0 + done + + # We don't have a match. If we have a file name that satisfies the path but + # that doesn't contain the requested symbol, we simply return that path. + + if [ "$depname" ] && [ "$depversion" ] + then + echo "$depname $depversion" + return 0 + fi + + # We don't have a matching filename under /System/Index nor under /Programs/*/*. + # What we do now is to query remote RPM/DEB databases to find which package hosts the + # dependency file. + + if ! Boolean "no-web" + then + Log_Normal "Searching the remote $(thirdparty_backend) database for the package hosting $originalpath" + depname=$(third_party_search_remotedb "$originalpath" "$arch" "$distro") + if [ "$depname" ] + then + # TODO: we could now lookup the GoboLinux recipe store to find whether we + # have it or not + echo "$(GuessProgramCase $depname)" + return 0 + fi + fi + fi +} + +function lookup_pkgname() { + local dependency="$1" + local pkgname=$(echo "$dependency" | cut -d'(' -f1) + + # GoboLinux doesn't have "devel" packages like most mainstream distros do + local goboname=$(echo "$pkgname" | sed 's,-devel$,,g') + + # Do we have a GoboLinux package installed with a matching name? + for testname in $(ls $goboPrograms/) + do + # Case-insensitive comparison (requires Bash 4) + if [ "${testname,,}" = "${pkgname,,}" ] || [ "${testname,,}" = "${goboname,,}" ] + then + echo "$testname" && return 0 + fi + done + + # Query the GoboLinux recipe store. + if ! Boolean "no-web" + then + + local recipeurl=$(FindPackage -t recipe "${pkgname}" || FindPackage -t recipe "${goboname}") + if [ "$recipeurl" ] + then + # TODO we're potentially discarding the wanted version(s) of the given dep + echo "$(basename $recipeurl | sed 's,\(.*\)--.*--.*,\1,g')" && return 0 + fi + fi + + # TODO Query the remote RPM database for $pkgname + + # TODO Query the remote DEB database for $pkgname + + return 1 +} + +function is_basic_symbol() { + local dependency="$1" + echo "$dependency" | grep -q "^rtld(" && return 0 + return 1 +} + +function deduce_program_name() { + local inputfile="$1" + local name=$(thirdparty_name "$inputfile") + echo $name +} + +function prepare_program_entry() { + local inputfile="$1" + + if [ ${#inputfiles[@]} -gt 1 ] && Is_Entry "app-name" && [ ! -z "$programname" ] + then + # We have already prepared this program's entry on /Programs + return + fi + if Is_Entry "app-name" + then programname=$(Entry "app-name") + else programname="$(deduce_program_name $inputfile)" + fi + if Is_Entry "version-number" + then programversion=$(Entry "version-number") + else programversion=$(printf "%s_%s" $(thirdparty_version "$inputfile") $(thirdparty_release "$inputfile")) + fi + + # Prepare /Programs tree and update program name (PrepareProgram may have changed its case) + PrepareProgram -t "$programname" "$programversion" + programname=$(ls $goboPrograms/ | grep -i "^${programname}$") + target="$goboPrograms/$programname/$programversion" +} + +### Operation ################################################################# + +Is_Writable "${goboPrograms}" || Verify_Superuser + +if Boolean "verbose" +then verbose="--verbose" +else verbose= +fi + +# The inputfiles array holds the full path of all RPM/DEB input files +# The inputnames array holds the package name of all RPM/DEB input files +inputfiles=() +inputnames=() +rpmcount=0 +debcount=0 +eval `Args_To_Array inputfiles_` +for entry in "${inputfiles_[@]}" +do + inputfiles+=( "$(readlink -f ${entry} || echo ${entry})" ) + inputnames+=( "$(rpminfo --name $entry)" ) + echo "$entry" | grep -qi "\.deb" && let debcount=debcount+1 + echo "$entry" | grep -qi "\.rpm" && let rpmcount=rpmcount+1 +done + +# Sanity checks, then import the backend to handle the package format +if [ $rpmcount -gt 0 ] && [ $debcount -gt 0 ] +then + Die "Error: cannot handle both RPM and DEB files in a single shot." +elif [ $rpmcount -gt 0 ] +then + Import RPM +elif [ $debcount -gt 0 ] +then + Import DEB +fi + +# These will be set by prepare_program_entry() +unset programname +unset programversion +unset target + +# Determine the flattening level of /opt. In other words, whether +# we have something like: +# /opt/pkgname/{bin,sbin...} (1-level), or +# /opt/vendorname/pkgname/{bin,sbin...} (2-level) + +flatteninglevel=$(determine_flattening_level) + +# Installation pipeline +for entry in "${inputfiles[@]}" +do + Log_Normal "Processing $(basename $entry)" + inputfile=$(fetch_package "$entry") + prepare_program_entry "$inputfile" + Quiet pushd "$target" || Die "Could not enter $target" + + uncompress_package "$inputfile" + flatten_package "$inputfile" + populate_resources "$inputfile" + + Quiet popd + Is_URL "$entry" && rm -f -- "$inputfile" +done + +# Symlinking +if [ $(Entry "symlink") = "no" ] +then + Log_Normal "Done." + exit 0 +fi + +[ -d "$target/Resources/Defaults/Settings" ] && UpdateSettings "$programname" "$programversion" +SymlinkProgram "$programname" "$programversion" +Log_Normal "Done." -- cgit v1.1