This week only. Pastebin PRO Accounts Christmas Special! Don't miss out!Want more features on Pastebin? Sign Up, it's FREE!
Guest

LjL

By: a guest on Jan 26th, 2010  |  syntax: Bash  |  size: 20.66 KB  |  views: 4,715  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. #!/bin/bash
  2.  
  3. # Copyright (C) 2007 Lorenzo J. Lucchini <ljlbox@tiscali.it>
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18. #
  19. #
  20. # First thing, we state that the directory where the sources reside is
  21. # the same as the argument the user passed to us. We will change this
  22. # later if needed, but since there is an "rm -r -f" that depends on
  23. # the contents of this variable, we better set it immediately.
  24.  
  25.  
  26.  
  27. ## Human language strings are stored in variables to ease a possible future localization.
  28.  
  29. LOC_SELECT_FILE="Select a file or directory"
  30. LOC_TYPE_FILE="Type the name of the archive or directory containing the source code"
  31. LOC_PRESS_ENTER_DEFAULT="Press Enter to use"
  32. LOC_PRESS_CTRL_D_TO_STOP="Press Ctrl-D when finished"
  33. LOC_TYPE_PASSWORD="This program is EXPERIMENTAL and DANGEROUS. Type your administrator password"
  34. LOC_INVALID_FILE="A file or directory name must be given"
  35. LOC_EXTRACTING="Extracting archive"
  36. LOC_EXTRACT_FAILED="Archive extraction failed"
  37. LOC_NO_BUILD_ENV="No known build environment found in source tree"
  38. LOC_UPDATING_AUTOAPT="Updating packages database"
  39. LOC_TRYING_CONFIGURE="Trying to configure"
  40. LOC_TRYING_MAKE="Trying to build"
  41. LOC_TRYING_INSTALL="Trying to install"
  42. LOC_CREATING_PACKAGE="Creating package"
  43. LOC_INSTALLING_DEPENDS="Installing runtime dependencies"
  44. LOC_APT_INSTALLING="APT is installing packages (do NOT interrupt)"
  45. LOC_APT_REMOVING="APT is removing packages (do NOT interrupt)"
  46. LOC_REMOVING_BUILD_DEPS="Removing build dependencies"
  47. LOC_SEARCHING_FILE="Searching for file"
  48. LOC_INSTALLING_PACKAGES="Installing package(s)"
  49. LOC_REMOVING_PACKAGES="Removing package(s)"
  50. LOC_FINISHED="Finished"
  51. LOC_RUNNING="Running"
  52. LOC_BUILD_COMPLETED="Finished building"
  53. LOC_BUILD_ABORTED="Build aborted. Incomplete build tree is in"
  54. LOC_UNEXPECTED_ABORT="Unexpected build system abort"
  55. LOC_FILE_NOT_FOUND="No such file or directory"
  56. LOC_CANNOT_READ="Cannot read"
  57. LOC_CONFIGURE_FAILED="Could not configure build system"
  58. LOC_BUILD_FAILED="Could not build program"
  59. LOC_INSTALL_FAILED="Could not install package"
  60. LOC_NOT_KNOWN_GUI="is not a known user interface"
  61. LOC_BAD_PASSWORD="Wrong password"
  62. LOC_REPORT_BUGS="Report bugs to <ljlbox@tiscali.it>"
  63. LOC_DESCRIPTION="Build the source tree contained in FILE (which can also be a directory)."
  64. LOC_USAGE="Usage"
  65. LOC_NOT_ROOT="You must be root to use this program"
  66. LOC_APT_FAILURE="APT reported a problem. Try 'apt-get -f install'"
  67.  
  68.  
  69.  
  70. ## Terminate with an error message (given as parameter), and remove any dangling
  71. ## build dependencies.
  72.  
  73. function Abort {
  74.     if [[ -n "$BUILD_DEPENDS" ]]; then
  75.         ## Avoid going into an infinite loop with RemovePackages
  76.         Packages=$BUILD_DEPENDS
  77.         BUILD_DEPENDS=""
  78.         RemovePackages $Packages
  79.     fi
  80.     if [[ -n "$1" ]]; then
  81.         ShowError "$1"
  82.     else
  83.         ShowError "$LOC_BUILD_ABORTED '$SOURCE_DIR'"
  84.     fi
  85.     exit 1
  86. }
  87.  
  88.  
  89. ## Execute a command (passed in the parameters) as user $USERNAME rather than root.
  90.  
  91. function UserMode {
  92.     su -c "$*" $USERNAME
  93. }
  94.  
  95.  
  96. ## Request a file or directory name to the user, and return it in the $File variable,
  97. ## or return the empty string if the user does not provide a valid filename.
  98.  
  99. function FileRequester {
  100.     if [[ $INTERFACE == "zenity" ]]; then
  101.         ## Unfortunately, Zenity doesn't allow chosing a file *or* a directory using
  102.         ## the same dialog. Here we just create a file selector dialog.
  103.         File=$(zenity 2>/dev/null --title "$LOC_SELECT_FILE" --file-selection)
  104.     elif [[ $INTERFACE == "kdialog" ]]; then
  105.         ## With KDialog, selecting a directory is not completely intuitive, but can
  106.         ## be done with the same dialog that allows selecting a file: select the
  107.         ## directory, and click 'Open' twice.
  108.         File=$(kdialog 2>/dev/null --getopenfilename $(pwd))
  109.     else
  110.         read -e -p "$LOC_TYPE_FILE : " File
  111.     fi
  112. }
  113.  
  114.  
  115.  
  116. ## Request a line of text from the user, and return it in the $String variable. The first
  117. ## argument is the prompt to the user, and the second argument, if given, is the
  118. ## default text.
  119.  
  120. function StringRequester {
  121.     if [[ $INTERFACE == "zenity" ]]; then
  122.         String=$(zenity 2>/dev/null --title "Input" --text "$1" --entry-text "$2" --entry)
  123.     elif [[ $INTERFACE == "kdialog" ]]; then
  124.         String=$(kdialog 2>/dev/null --title "Input" --inputbox "$1" "$2")
  125.     else
  126.         read -p "$1 - $LOC_PRESS_ENTER_DEFAULT '$2' : " String
  127.         if [[ -z "$String" ]]; then String="$2"; fi
  128.     fi
  129. }
  130.  
  131.  
  132. ## Request multi-line text to the user, and return it in the $Text variable. The first
  133. ## argument is the prompt to the user, and the second argument, if given, is the
  134. ## default text.
  135.  
  136. function TextRequester {
  137.     if [[ $INTERFACE == "zenity" ]]; then
  138.         ## Zenity currently doesn't support '--text' for multiline input boxes, it just ignores
  139.         ## it. We put it there anyway, in the hope that it will supported in the future.
  140.         Text=$(echo "$2" | zenity 2>/dev/null --title "Input" --text "$1" --text-info --editable)
  141.     elif [[ $INTERFACE == "kdialog" ]]; then
  142.         Text=$(kdialog 2>/dev/null --title "Input" --textinputbox "$1" "$2")
  143.     else
  144.         read -d $'\x4' -p "$1 - $LOC_PRESS_CTRL_D_TO_STOP :"$'\n' Text
  145.         echo ""
  146.         if [[ -z "$Text" ]]; then Text="$2"; fi
  147.     fi
  148. }
  149.  
  150.  
  151. ## Show an error message (passed via the first parameter) to the user, and terminate.
  152.  
  153. function ShowError {
  154.     echo ERROR: "${1}."
  155.     HideStatus
  156.     HideNotification
  157.     if [[ $INTERFACE == "zenity" ]]; then
  158.         zenity 2>/dev/null --error --text "${1}."
  159.     elif [[ $INTERFACE == "kdialog" ]]; then
  160.         kdialog 2>/dev/null --error "${1}."
  161.     fi
  162. }
  163.  
  164.  
  165. ## Show an empty status window with a progressbar starting at 0%.
  166.  
  167. function ShowStatus {
  168.     if [[ $INTERFACE == "zenity" ]]; then
  169.         exec 8> >(zenity 2>/dev/null --width 550 --height 250 --progress --auto-kill --text '\n\n\n\n\n\n\n ')
  170.     elif [[ $INTERFACE == "kdialog" ]]; then
  171.         DCOP_REFERENCE=$(kdialog 2>/dev/null --geometry 550x250 --title "AutoDeb" --progressbar "")
  172.         dcop $DCOP_REFERENCE showCancelButton true
  173.         dcop $DCOP_REFERENCE setAutoClose true
  174.     fi
  175. }
  176.  
  177.  
  178. ## Close a previously opened status window.
  179.  
  180. function HideStatus {
  181.     if [[ $INTERFACE == "zenity" ]]; then
  182.         exec >&8-
  183.     elif [[ $INTERFACE == "kdialog" ]]; then
  184.         if [[ -n "$DCOP_REFERENCE" ]]; then dcop $DCOP_REFERENCE close; fi
  185.     fi
  186. }
  187.  
  188.  
  189. ## With a status window open, update it as follows:
  190. ## - 1st parameter: description of the current state
  191. ## - 2nd parameter: a percentage, or none to leave percentage unchanged
  192. ## - 3rd parameter: a file descriptor to read sub-states from
  193. ## If the 3rd parameter is given, lines of text can be piped into the file
  194. ## descriptor to make the sub-state description change accordingly in real time.
  195.  
  196. function UpdateStatus {
  197.     echo >>"${LOG_FILE}" -e "\n$(date): $1\n"
  198.     if [[ -n "$2" ]]; then echo "${1}... (${2}%)"; else echo "${1}..."; fi
  199.     if [[ $INTERFACE == "zenity" ]]; then
  200.         echo >&8 '# '"${1}..."'\n\n\n\n\n\n\n '
  201.         echo >&8 "$2"
  202.         if [[ -n "$3" ]]; then
  203.             ## Using sed to prepend text is far from foolproof. I don't know of a better way; meanwhile, we just
  204.             ## hope that our program won't output pipe characters.
  205.             tee <"$3" --append "${LOG_FILE}" | sed >&8 --unbuffered --regexp-extended \
  206.                 's|^[[:space:]]*(.{0,70})(.{0,70})(.{0,70})(.{0,70}).*|# '"${1}..."'\\n\\n\1\\n\2\\n\3\\n\4\\n\\n |g'
  207.         fi
  208.     elif [[ $INTERFACE == "kdialog" ]]; then
  209.         dcop "$DCOP_REFERENCE" setLabel "$(echo -e "\n${1}...\n\n\n\n\n\n ")"
  210.         if [[ -n "$2" ]]; then dcop "$DCOP_REFERENCE" setProgress "$2"; fi
  211.         if [[ -n "$3" ]]; then
  212.             (echo -e -n "\n" ; tee <"$3" --append "${LOG_FILE}" | sed --unbuffered --regexp-extended \
  213.                 's|^[[:space:]]*(.{0,70})(.{0,70})(.{0,70})(.{0,70}).*|'"${1}..."'\n\n\1\n\2\n\3\n\4\n \x0|g' ) | \
  214.                 xargs -n 1 -0 --replace dcop "$DCOP_REFERENCE" setLabel "{}"
  215.         fi
  216.     else
  217.         if [[ -n "$3" ]]; then tee <"${3}" --append "${LOG_FILE}" | cat; fi
  218.     fi
  219. }
  220.  
  221.  
  222. ## Show a notification (try icon, balloon... depends on the interface), taking the
  223. ## text from the first parameter.
  224.  
  225. function ShowNotification {
  226.     echo "Warning: $1"
  227.     if [[ $INTERFACE == "zenity" ]]; then
  228.         exec 9> >(zenity 2>/dev/null --listen --text "$1" --notification)
  229.     elif [[ $INTERFACE == "kdialog" ]]; then
  230.         kdialog 2>/dev/null --passivepopup "$1" 5 &
  231.     fi
  232. }
  233.  
  234.  
  235. ## Remove a previously shown notification.
  236.  
  237. function HideNotification {
  238.     if [[ $INTERFACE == "zenity" ]]; then
  239.         exec 9>&-
  240.     fi
  241. }
  242.  
  243.  
  244. ## Install the Debian packages given as parameters.
  245.  
  246. function InstallPackages {
  247.     ShowNotification "$LOC_APT_INSTALLING"
  248.     apt-get -qq install $* | UpdateStatus "$LOC_INSTALLING_PACKAGES $*..." "" /dev/stdin
  249.     if [[ $PIPESTATUS -ne 0 ]]; then Abort "$LOC_APT_FAILURE"; fi
  250.     HideNotification
  251. }
  252.  
  253.  
  254. ## Remove the Debian packages given as parameters.
  255.  
  256. function RemovePackages {
  257.     ShowNotification "$LOC_APT_REMOVING"
  258.     apt-get -qq remove $* | UpdateStatus "$LOC_REMOVING_PACKAGES $*" 90 /dev/stdin
  259.     if [[ $PIPESTATUS -ne 0 ]]; then Abort "$LOC_APT_FAILURE"; fi
  260.     HideNotification
  261. }
  262.  
  263.  
  264. ## Ask the user to fill in metadata for the package, based on a string (passed
  265. ## as parameter) in the form programname-version.number.
  266.  
  267. function SetPackageInfo {
  268.     PKG_NAME=$(echo "$1" | rev | cut -d "-" -f 2- | rev | sed 's/ /_/g')
  269.     PKG_VERS=$(echo "$1" | rev | cut -d "-" -f 1  | rev | sed 's/ /_/g')
  270.     StringRequester "Choose a name for the package to create" $PKG_NAME; PKG_NAME="$String"
  271.     StringRequester "Type a a version number for the package" $PKG_VERS; PKG_VERS="$String"
  272.     TextRequester "Type in a description of the package" "$PKG_NAME $PKG_VERS, generated by AutoDeb"; PKG_DESC="$Text"
  273. }
  274.  
  275.  
  276. ## Run the command that was passed as parameter which keeping track of any files
  277. ## it tries to access. If the command fails (non-zero exit status), install packages
  278. ## that may fix the problem, and try again. Keep doing this until out of ideas about
  279. ## what more to install.
  280.  
  281. function MonitoredExecute {
  282.     true; while [[ $? -eq 0 ]]; do
  283.         TRACE_FILE=$(tempfile)
  284.  
  285.         UserMode strace -f -F -q -e trace=file,exit_group -e signal=none "$1" \
  286.             2> >(grep 'ENOENT\|exit\|pkg\-config' >${TRACE_FILE}) | \
  287.             UpdateStatus "$LOC_RUNNING '${1}'" "" /dev/stdin
  288.  
  289.         ## Since strace discards the traced program's exit value, we need another way
  290.         ## to know if the configure was successful; so we explicitely asked strace to
  291.         ## store any exit_group call that was made, and now we read the argument that
  292.         ## was passed to last one made.
  293.         if ! tail -1 $TRACE_FILE | grep -q 'exit'; then
  294.             Abort "$LOC_UNEXPECTED_ABORT"
  295.         elif tail -1 $TRACE_FILE | grep -q '0'; then
  296.             ## Configure was successful, leave the loop
  297.             break
  298.         fi
  299.  
  300.         ## If we reach this point, it means the build was unsuccessful, so we look at
  301.         ## the strace output and install a package we "guess" as the culprit.
  302.  
  303.         ProcessTraceFile $TRACE_FILE
  304.     done
  305.     return $Status
  306. }
  307.  
  308.  
  309. ## Given a filename, passed as parameter, find a package containing it (if multiple packages
  310. ## contain the same file, heuristics are used and only one is chosen) and return it in the
  311. ## variable $Package. If no package is found, an empty string is returned.
  312.  
  313. function FindPackageForFile {
  314.     UpdateStatus "$LOC_SEARCHING_FILE '${1}'"
  315.     ## auto-apt can take full pathames using the 'check' command, or regexps with 'search'
  316.     if [[ "${1:0:1}" == "/" ]]; then
  317.         Packages=$(auto-apt check "$1" | tr "," " ")
  318.     else
  319.         Packages=$(auto-apt search "${1}[^[:print:]]" | cut -f 2 | tr "\n" "," | tr "," " ")
  320.     fi
  321.     ## auto-apt check outputs '*' when it cannot find anything.
  322.     if [[ "$Packages" == '*' ]]; then Package=""; return 1; fi
  323.     ## If multiple packages provide the same file, choose the one with the shortest name.
  324.     ## This helps making the right choice between 'package' and 'package-dbg', and the like.
  325.     Package=""
  326.     for Candidate in $Packages; do
  327.         if [[ -z "$Package" || ${#Candidate} -lt ${#Package} ]]; then Package=$Candidate; fi
  328.     done
  329.     Package="$(basename "$Package")"
  330.     if [[ -n "$Package" ]]; then return 0; else return 1; fi
  331. }
  332.  
  333.  
  334. ## Parse a file given as argument containing strace output for files not found during a build attempt,
  335. ## and install the Debian package judged most relevant to solve the problem. Return 0 if something was
  336. ## installed, 1 otherwise.
  337.  
  338. function ProcessTraceFile {
  339.  
  340.     ## Read the strace output and find out which files could not be accessed. Sort them heuristically
  341.     ## so as to have the most likely candidates for missing packages listed first.
  342.  
  343.     MainStatCalls=$(tac $1 | grep -v '\[' | grep    stat | head -40 | grep --only-matching '"/[^" ]\+"')
  344.     ForkStatCalls=$(tac $1 | grep    '\[' | grep    stat | head -40 | grep --only-matching '"/[^" ]\+"')
  345.     MainMiscCalls=$(tac $1 | grep -v '\[' | grep -v stat | head -40 | grep --only-matching '"/[^" ]\+"')
  346.     ForkMiscCalls=$(tac $1 | grep    '\[' | grep -v stat | head -40 | grep --only-matching '"/[^" ]\+"')
  347.     PkgConfigCalls=$(tac $1 | grep 'exec' | grep 'pkg-config' | grep --only-matching '"[^-/",][^/", ]\+[" ]' | sed 's/[" ]$/.pc"/g')
  348.  
  349.     FileList=$(echo "$PkgConfigCalls $MainStatCalls $ForkStatCalls $MainMiscCalls $ForkMiscCalls" | sed 's:"::g')
  350.  
  351.     for File in $FileList; do
  352.         ## If the file is in the blacklist, skip it
  353.         for ToSkip in $BLACKLIST; do if [[ "$File" == "$ToSkip" ]]; then continue; fi; done
  354.         if echo "$File" | grep -q '^/tmp\|^/home\|^/usr/local'; then continue; fi
  355.         FindPackageForFile $File
  356.         if [[ -z $Package ]]; then
  357.             ## No package owns that file, so we put the file into the blacklist in order to
  358.             ## avoid wasting time searching for it again.
  359.             BLACKLIST="$BLACKLIST $File"
  360.         else
  361.             ## Install the found package, add it to the list of install development packages
  362.             ## and terminate successfully.
  363.             InstallPackages $Package
  364.             BUILD_DEPENDS="$BUILD_DEPENDS $Package"
  365.             return 0
  366.         fi
  367.     done
  368.  
  369.     return 1
  370. }
  371.  
  372.  
  373.  
  374. ## Give some help if requested.
  375.  
  376. if [[ "$1" == "--help" ]]; then
  377.     echo "$LOC_USAGE: $0 [--kde|--gnome] [FILE]"
  378.     echo "$LOC_DESCRIPTION"
  379.     echo ""
  380.     echo "$LOC_REPORT_BUGS"
  381.     exit 0
  382. fi
  383.  
  384.  
  385. ## The preferred user interface can be specified in the command line, prefixed with '--'.
  386.  
  387. if [[ "${1:0:2}" == "--" ]]; then
  388.     INTERFACE="${1:2}"
  389.     shift
  390. fi
  391.  
  392.  
  393. ## The following doesn't really do very much at the moment, except fall back to the console
  394. ## interface if whatever the user specified is not available. In the future, it might provide
  395. ## a more sophisticated fallback mechanism, in case more interface choices are added.
  396.  
  397. while ! which "$INTERFACE" >/dev/null; do
  398.     if [[ "$INTERFACE" == "" ]]; then break
  399.     elif [[ "$INTERFACE" == "kde" ]];     then INTERFACE="kdialog"
  400.     elif [[ "$INTERFACE" == "gnome" ]];   then INTERFACE="zenity"
  401.     elif [[ "$INTERFACE" == "kdialog" ]]; then INTERFACE=""
  402.     elif [[ "$INTERFACE" == "zenity" ]];  then INTERFACE=""
  403.     else Abort "'$INTERFACE' $LOC_NOT_KNOWN_GUI"
  404.     fi
  405. done
  406.  
  407.  
  408. ## Check that we are root.
  409.  
  410. if [[ $UID -ne 0 ]]; then
  411.     Abort "$LOC_NOT_ROOT"
  412. fi
  413.  
  414.  
  415. ## If no argument was passed, then ask the user where the source tarball, or directory, is located.
  416.  
  417. if [[ -e "$1" ]]; then
  418.     SOURCE_PTR="$1"
  419. else
  420.     FileRequester; SOURCE_PTR="$File"
  421. fi
  422.  
  423. if [[ ! -e "$SOURCE_PTR" ]]; then Abort "$LOC_FILE_NOT_FOUND '${SOURCE_PTR}'"; fi
  424. if [[ ! -r "$SOURCE_PTR" ]]; then Abort "$LOC_CANNOT_READ '${SOURCE_PTR}'"; fi
  425.  
  426.  
  427. ## We'll need to know which directory we started from later, and we also need
  428. ## a $LOG_FILE variable for UpdateStatus to work.
  429.  
  430. WORK_DIR=$(pwd)
  431. LOG_FILE="$WORK_DIR/autodeb.log"
  432.  
  433.  
  434. ## If the user gave a directory, use it; if a file was given, attempt to
  435. ## extract it using tar into a temporary directory, and use that.
  436.  
  437. USERNAME=$(stat -c "%U" "$SOURCE_PTR")
  438.  
  439. if [[ -z "$SOURCE_PTR" ]]; then
  440.     Abort "$LOC_INVALID_FILE"
  441. elif [[ -d "$SOURCE_PTR" ]]; then
  442.     SOURCE_DIR="$SOURCE_PTR"
  443.     SetPackageInfo $(cd "$SOURCE_DIR"; basename $(pwd))
  444.     ShowStatus
  445. else
  446.     SOURCE_DIR=$(UserMode mktemp -d)
  447.     SetPackageInfo $(basename $SOURCE_PTR | sed 's/\.tar$\|\.tar\.gz$\|\.tar\.bz2$\|\.tgz$\|\.zip$//')
  448.     ShowStatus
  449.     UpdateStatus "$LOC_EXTRACTING" 0
  450.     UserMode tar --extract --directory "$SOURCE_DIR" --file "$SOURCE_PTR"
  451.     if [[ $? -ne 0 ]]; then
  452.         Abort "$LOC_EXTRACT_FAILED"
  453.     fi
  454.     ## If the tar archive created one single sub-directory, use that.
  455.     ## Otherwise, just hope the parent directory is the right one.
  456.     if [[ $(ls ${SOURCE_DIR} | wc -l) -eq 1 ]]; then
  457.         SOURCE_DIR=$(echo "${SOURCE_DIR}"/*)
  458.     fi
  459. fi
  460.  
  461.  
  462. cd "$SOURCE_DIR"
  463.  
  464. trap Abort 1 2 3 4 5 6 7 8 9
  465.  
  466.  
  467. ## The "batch job" starts here, and we try to avoid having to stop to ask the user
  468. ## things from now on.
  469.  
  470. ## 1) Install essential packages
  471.  
  472. InstallPackages build-essential auto-apt checkinstall
  473.  
  474. ## Create the auto-apt database if it doesn't yet exist
  475. if ! ( auto-apt list | head -1 >/dev/null ); then
  476.     auto-apt update | UpdateStatus "$LOC_UPDATING_AUTOAPT" 5 /dev/stdin
  477. fi
  478.  
  479.  
  480. ## 1) Find out what build system the program uses
  481.  
  482. if [[ -x "configure" ]]; then
  483.     CONF_CMD="./configure"
  484.     MAKE_CMD="make"
  485.     INST_CMD="make install"
  486. else
  487.     Abort "$LOC_NO_BUILD_ENV"
  488. fi
  489.  
  490.  
  491. ## 2) Configure build environment
  492.  
  493. UpdateStatus "$LOC_TRYING_CONFIGURE" 10
  494. MonitoredExecute $CONF_CMD
  495. if [[ $? -ne 0 ]]; then Abort "$LOC_CONFIGURE_FAILED"; fi
  496.  
  497.  
  498. ## 3) Build program
  499.  
  500. UpdateStatus "$LOC_TRYING_MAKE" 20
  501. MonitoredExecute $MAKE_CMD
  502. if [[ $? -ne 0 ]]; then Abort "$LOC_BUILD_FAILED"; fi
  503.  
  504.  
  505. ## 4) Install program
  506.  
  507. PKG_DIR=$(UserMode mktemp -d)
  508. UserMode checkinstall --default --install=no --pakdir "$PKG_DIR" \
  509.     --pkgname "$PKG_NAME" --pkgversion "$PKG_VERS" $INST_CMD | \
  510.     UpdateStatus "$LOC_TRYING_INSTALL" 50 /dev/stdin
  511. if [[ $PIPESTATUS -ne 0 ]]; then Abort "$LOC_INSTALL_FAILED"; fi
  512.  
  513.  
  514. ## 5) Find runtime dependencies
  515.  
  516. for File in $(UserMode dpkg --vextract "$PKG_DIR/"*.deb "$PKG_DIR"); do
  517.     ## Right now, we only care about executable files.
  518.     ## One could think about later implementing heuristics to extract
  519.     ## meaningful dependencies from other filetypes, too... who knows.
  520.     File="${PKG_DIR}/${File}"
  521.     if ! ( file --mime "$File" | grep -q "x-executable" ); then continue; fi
  522.     echo "$File" | UpdateStatus "Finding runtime dependencies..." 60 /dev/stdin
  523.     RequiredLibraries="$RequiredLibraries $(ldd -u $File | awk '{ print $1 }' | tail -n +3)"
  524. done
  525. RequiredLibraries=$(echo "${RequiredLibraries}" | sort | uniq)
  526. for File in $RequiredLibraries; do
  527.     FindPackageForFile "$(basename "$File")"
  528.     if [[ $? -eq 0 ]]; then RUN_DEPENDS="$RUN_DEPENDS, $Package"; fi
  529. done
  530. ## Remove duplicates. More elegant ways to achieve that are welcome.
  531. RUN_DEPENDS="$(echo "${RUN_DEPENDS#, }" | tr " " "\n" | sort | uniq | tr "\n" " ")"
  532.  
  533.  
  534. ## 6) Create package
  535.  
  536. echo "$PKG_DESCRIPTION" | \
  537.     checkinstall --default --install=no --pakdir "$PKG_DIR" --pkgname "$PKG_NAME" \
  538.     --pkgversion "$PKG_VERS" --requires "$RUN_DEPENDS" $INST_CMD | \
  539.     UpdateStatus "$LOC_CREATING_PACKAGE" 70 /dev/stdin
  540.  
  541.  
  542. ## 7) Install package and dependencies
  543.  
  544. dpkg -i "$PKG_DIR/"*.deb 2>/dev/null | UpdateStatus "$LOC_INSTALLING_DEPENDS" 80 /dev/stdin
  545. apt-get -qq -f install | UpdateStatus "$LOC_INSTALLING_DEPENDS" 85 /dev/stdin
  546. if [[ $PIPESTATUS -ne 0 ]]; then Abort "$LOC_APT_FAILURE"; fi
  547.  
  548.  
  549. ## 8) Remove build dependencies
  550.  
  551. if [[ -n "$BUILD_DEPENDS" ]]; then
  552.     UpdateStatus "$LOC_REMOVING_BUILD_DEPS" 90
  553.     RemovePackages $BUILD_DEPENDS
  554. fi
  555.  
  556.  
  557. ## 9) Clean up
  558.  
  559. UserMode cp "$PKG_DIR/"*.deb "$WORK_DIR"/
  560. UpdateStatus $LOC_FINISHED 100
  561. HideStatus
  562. ShowNotification "$LOC_BUILD_COMPLETED '${PKG_NAME}'"
clone this paste RAW Paste Data