AutoDeb
4312
Comment:
|
19648
|
Deletions are marked like this. | Additions are marked like this. |
Line 61: | Line 61: |
----------------------------------------- #!/bin/sh # Copyright (C) 2005 Lorenzo J. Lucchini <ljlbox@tiscali.it> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # First thing, we state that the directory where the sources reside is # the same as the argument the user passed to us. We will change this # later if needed, but since there is an "rm -r -f" that depends on # the contents of this variable, we better set it immediately. SOURCE_DIR="$1" # This function is called when a signal is sent, or an error occurs.. function Abort { echo "Aborting..." RestoreState exit 1 } function RestoreAPT { if [ "${DevDepends}" != "" ]; then echo "Removing development dependencies:" echo "${DevDepends}" echo "" sudo aptitude markauto ${DevDepends} 2>/dev/null else echo "No dependencies to remove." return fi } # This function is called when the program terminates, normally or abnormally. function RestoreState { echo "" sudo mv /usr/lib/auto-apt/auto-apt-installer.orig /usr/lib/auto-apt/auto-apt-installer rm "${DEPENDS_LOG}" rm "${SHORT_DB}" if [ "${SOURCE_DIR}" != "$1" ]; then #rm -r -f "${SOURCE_DIR}" true fi RestoreAPT } function Usage { echo "Usage: ${0} FILE" echo "" echo "FILE can be a .gz archive, a .bz2 archive, a .tar archive, or a directory." echo "" echo "Report bugs to <ljlbox@tiscali.it>." echo "Include \"autodeb\" in the subject." exit 0 } if [ ! -e "$1" ]; then Usage fi # We ask for the sudo password now, so that the user won't be presented with sudo pop-ups # later. The password will only be remembered for a set time (15 minutes on my system), # after which it will be asked again, but hopefully, that time will be more than enough. # The worst that can happen is that the password will be asked again later. echo "Please type your sudo password now." echo "It will be needed during the process in order to install packages." echo "" echo "If you have a CD-ROM (or other removable media) listed in your" echo "/etc/apt/sources.list, please insert it now." echo "" echo "THIS SOFTWARE IS EXPERIMENTAL." echo "IT MIGHT DAMAGE YOUR SYSTEM." echo "USE AT YOUR OWN RISK!" echo "Type CTRL-D now to exit." sudo -k; sudo -v if [ $? -ne 0 ]; then echo "Exiting." exit 1 fi echo "" # If the argument that was feeded to us is an archive, we extract it to # a temporary directory. If it's a directory, we just use that. if echo "$1" | grep ".bz2$" >/dev/null; then SOURCE_DIR=`mktemp -d` ArchiveName=`basename "$1" .tar.bz2` echo "Unpacking bzip2 archive..." bunzip2 "$1" --stdout | tar -x --directory "${SOURCE_DIR}" if [ $? -ne 0 ]; then echo "Could not extract ${1}." Abort fi elif echo "$1" | grep ".tar.gz$\|.tgz" >/dev/null; then SOURCE_DIR=`mktemp -d` if echo "$1" | grep ".tar.gz$" >/dev/null; then ArchiveName=`basename "$1" .tar.gz` else ArchiveName=`basename "$1" .tgz` fi echo "Unpacking gzip archive..." gunzip "$1" --stdout | tar -x --directory "${SOURCE_DIR}" if [ $? -ne 0 ]; then echo "Could not extract ${1}." Abort fi elif echo "$1" | grep ".tar$" >/dev/null; then SOURCE_DIR=`mktemp -d` ArchiveName=`basename "$1" .tar` echo "Unpacking tar archive..." cat "$1" | tar -x --directory "${SOURCE_DIR}" if [ $? -ne 0 ]; then echo "Could not extract ${1}." Abort fi elif [ -d "$1" ]; then SOURCE_DIR="$1" ArchiveName=`basename "${SOURCE_DIR}"` else Usage fi # If the .tar archive created one single sub-directory, use that. # If it didn't, try using the base directory, and hope there is "configure" and # a "Makefile" there. if [ `ls -1 ${SOURCE_DIR} | wc -l` == "1" ]; then SOURCE_DIR="${SOURCE_DIR}/*" fi # Try to guess the package name and version. # (name is what comes before a hypen, version is what comes after) PackageName=`echo "${ArchiveName}" | rev | cut -d "-" -f 2- | rev` PackageVersion=`echo "${ArchiveName}" | rev | cut -d "-" -f1 | rev` # If there is no version, we invent one. if [ "${PackageName}" == "${PackageVersion}" ]; then PackageVersion="1.0" fi # We don't really trust our guess, so we ask the user, who can decide whether to accept # our guesses (press Enter) or type a custom name and version. Some basic sanity checks # are performed on the user input. read -e -p "Package name - press Enter to use '${PackageName}' : " if [ "${REPLY}" != "" ]; then if [ `expr "${REPLY}" : " "` -ne 0 ]; then echo "Bad package name - must not contain spaces." Abort fi PackageName="${REPLY}" fi read -e -p "Package version - press Enter to use '${PackageVersion}' : " if [ "${REPLY}" != "" ]; then if [[ `expr "${REPLY}" : " "` -ne 0 && `expr "${REPLY} : "[0123456789]"` -ne 0 ]]; then echo "Bad package version - must start with a digit." Abort fi PackageVersion="${REPLY}" fi # We set some variables that will come useful later. CUSTOM_DIR="`pwd`/autodeb-${PackageName}-${PackageVersion}" DEPENDS_LOG=`tempfile` SHORT_DB=`tempfile` # Create the output directory mkdir "${CUSTOM_DIR}" if [ ! -d "${CUSTOM_DIR}" -o ! -w "${CUSTOM_DIR}" ]; then echo "Could not create output directory ${CUSTOM_DIR}." echo "Do you have write permissions for `pwd`?" Abort fi # We check that auto-apt and checkinstall are installed, and install them if they aren't. if [ ! -e /usr/bin/auto-apt ]; then echo ""; echo "Installing auto-apt..." sudo aptitude -y install auto-apt if [ $? -ne 0 ]; then echo "Installing auto-apt failed." echo "You need to have auto-apt installed for autodeb to work." Abort fi echo "Updating auto-apt's database..." sudo auto-apt update 2>/dev/null if [ $? -ne 0 ]; then echo "Could not update auto-apt's database." echo "Try running 'auto-apt update' manually." Abort fi fi if [ ! -e /usr/bin/checkinstall ]; then echo ""; echo "Installing checkinstall..." sudo aptitude -y install checkinstall if [ $? -ne 0 ]; then echo "Installing checkinstall failed." echo "You need to have checkinstall installed for autodeb to work." Abort fi fi # Install build-essential. Besides the fact that this package is needed for compiling # just about anything, this also has the purpose of making aptitude run interactively, # so that if there are any bad problems in the APT database, the user will be notified. # Later, we use the option '--q', which could do bad stuff without telling the user. echo ""; echo "Initializing package system..." sudo aptitude install build-essential if [ $? -ne 0 ]; then echo "Could not initialize package system." Abort fi echo ""; echo "Trimming auto-apt database..." auto-apt list | grep "\-dev" | cut -f -10 -d "," | auto-apt -D "${SHORT_DB}" merge # From now on, we need to clean up stuff (restore auto-apt-installer, remove installed # packages) if we exit prematurely. trap Abort 1 2 3 4 5 6 7 8 9 # Unfortunately, we must hack into auto-apt to get a sane behavior. # We don't want auto-apt to immediately install every package that it thinks 'configure' # is wanting, because many of those are not real dependencies, but just things that # 'configure' looks for by default, such as the G77 Fortran compiler (and worse). # Also, I have experienced many weird behaviors with auto-apt. # # What we do, instead, is make auto-apt just *list* the packages it would install, # and every time 'configure' fails, we only install *the last package* that was # requested before 'configure' failed. We then re-run 'configure'. Slower, but # hopefully saner. # # Now, in order to be able to do this, we must modify the auto-apt-installer script, # to make it just list packages instead of going on an presenting a GTK interface # asking the user to install them. # We backup the original script, and we'll restore it later after we've finished # using auto-apt. if [ ! -e /usr/lib/auto-apt/auto-apt-installer.orig ]; then sudo mv /usr/lib/auto-apt/auto-apt-installer /usr/lib/auto-apt/auto-apt-installer.orig if [ $? -ne 0 ]; then echo "Could not make a backup copy of /usr/lib/auto-apt/auto-apt-installer." Abort fi else echo "A backup copy of /usr/lib/auto-get/auto-apt-installer already exists, continuing." fi sudo sh -c "echo >/usr/lib/auto-apt/auto-apt-installer \"#!/bin/sh\"" sudo sh -c "echo >>/usr/lib/auto-apt/auto-apt-installer \"echo >${DEPENDS_LOG} \\\$3\"" sudo chmod --reference=/usr/lib/auto-apt/auto-apt-installer.orig /usr/lib/auto-apt/auto-apt-installer cd ${SOURCE_DIR} if [ ! -e configure ]; then echo "No configure script was found." echo "Your program may have to be configured in non-standard ways." Abort fi echo "" DBOption="-D ${SHORT_DB}" while true; do echo -n "Trying to configure program... " auto-apt ${DBOption} run ./configure 1>"${CUSTOM_DIR}/configure.log" 2>"${CUSTOM_DIR}/configure.log" if [ $? -ne 0 ]; then PreviousDependency="${Dependency}" Dependency=`tail -1 ${DEPENDS_LOG} | rev | cut -f1 -d "/" | rev` if [ "${Dependency}" == "${PreviousDependency}" ]; then if [ "${DBOption}" != "" ]; then echo "Package not found, trying with full database... " DBOption="" continue else echo "Cannot configure. See configure.log." Abort fi fi echo "${Dependency} missing, installing..." DevDepends="${DevDepends} ${Dependency}" # We don't want aptitude to prompt the user (as the output is redirected); instead, we want # aptitude to just exit whenever a prompt would be displayed. Using apt-get, this can be done # by means of the "-qq" option, but with aptitude, I needed to resort to the following hack. sudo sh -c "yes 'No' | aptitude -y install ${Dependency} 1>'${CUSTOM_DIR}/apt.log' 2>'${CUSTOM_DIR}/apt.log'" if [ $? -ne 0 ]; then echo "Could not install ${Dependency}." echo "APT error. See apt.log." Abort fi else break fi DBOption="-D ${SHORT_DB}" done echo "successful." # Now let's compile! echo ""; echo "Compiling program..." make 1>"${CUSTOM_DIR}/make.log" 2>"${CUSTOM_DIR}/make.log" if [ $? -ne 0 ]; then echo "Could not compile." echo "Error during 'make', see make.log." Abort fi # We now 'make install' using checkinstall to create a .deb package containing # the files that 'make install' creates. echo ""; echo "Installing program..." sudo checkinstall --default --pkgname="${PackageName}" --pkgversion="${PackageVersion}" >"${CUSTOM_DIR}/install.log" if [ $? -ne 0 ]; then echo "Could not install." echo "Checkinstall may have encountered an error, see install.log." Abort fi echo ""; echo "Finding runtime dependencies..." for File in `dpkg -L ${PackageName}`; do NewLibList=`ldd ${File} 2>/dev/null` if [ $? -eq 0 ]; then NewLibList=`echo "$NewLibList" | awk ' { print $1 } '` LibList="${LibList} ${NewLibList}" fi done # Prune the list of libraries to remove duplicates, and turn it from the ugly # ldd output format into a clean, tidy list of filenames. LibList=`echo "${LibList}" | sort | uniq | awk ' { print $1 } '` # Now find the packages that correspond to the listed libraries... echo -n "Finding packages containing dependencies..." for Lib in ${LibList}; do LibName=`basename ${Lib}` NewDependency=`auto-apt search ${LibName} | rev | cut -f1 -d "/" | rev` # Sometimes auto-apt reports the same dependency more than once, if more than # one file has the same name in a package; hence the "sort | uniq". NewDependency=`echo "${NewDependency}" | sort | uniq | tr -s "\n" "|" | rev | cut -b 2- | rev` if echo "${NewDependency}" | grep "|" >/dev/null; then # This is a multi-package dependency. If one of the packages it lists # is already listed in a single-package dependency, then ignore this # dependency. Redundant=0 for Package in `echo ${NewDependency} | tr -s "|" " "`; do if echo "$BinDepends" | grep "[ ^]${Package}[ $]" >/dev/null; then Redundant=1 fi done if [ ${Redundant} -eq 0 ]; then BinDepends="${BinDepends} ${NewDependency}" fi else # This is a single-package dependency. Remove any multi-package # dependencies listing the same package as this dependency. BinDepends=`echo "$BinDepends" | \ sed "s/[ ^][^ ]+|${NewDependency}[ $]//g" | \ sed "s/[ ^]${NewDependency}|[^ ]+[ $]//g" | \ sed "s/[ ^][^ ]+|${NewDependency}|[^ ]+[ $]//g" ` BinDepends="${BinDepends} ${NewDependency}" fi echo -n "." done echo "" # Then remove duplicate packages... BinDepends=`echo "${BinDepends}" | tr " " "\n" | sort | uniq | tr -s "\n" " "` BinDepends=`echo "${BinDepends}" | tr -s " " | sed "s/^ //" | sed "s/ $//" | sed "s/ /, /g"` echo "Your program was found to depend on the following packages:" echo "${BinDepends}" # Then store the packages left as runtime dependencies for the user's program. for DEB_PACKAGE in ${PackageName}_${PackageVersion}*.deb; do OLD_DIR=`pwd` mv "${DEB_PACKAGE}" "${CUSTOM_DIR}/" cd "${CUSTOM_DIR}" ar x "${DEB_PACKAGE}" tar zxf "control.tar.gz" TailLineNumber=`grep --line-number "^Description:" "control" | cut -f1 -d ":"` HeadLineNumber=`echo "${TailLineNumber} - 1" | bc` NewControl=`head -n ${HeadLineNumber} "control"; echo "Depends: ${BinDepends}"; tail -n +${TailLineNumber} "control"` echo "${NewControl}" >"control" tar zcf "control.tar.gz" "control" ar rc "${DEB_PACKAGE}" control.tar.gz data.tar.gz # We install the package. At this point, it will probably complain about missing dependencies. sudo dpkg -i "${DEB_PACKAGE}" 1>/dev/null 2>/dev/null # But then we run aptitude, which will install those and mark them as automatic. echo ""; echo -n "Installing the following NEW packages:" sudo aptitude -y install "${PackageName}" | grep "^ " | tail -1 cd "${OLD_DIR}" echo "" done RestoreState echo "" echo "Your program has been succesfully installed, and a .deb package has been created." echo "You can find it inside" echo " ${CUSTOM_DIR}" echo "and in the same directory you will also find the following log files:" echo " apt.log" echo " configure.log" echo " make.log" echo " install.log" echo "" exit 0 ----------------------------------------- |
AutoDeb is an EXPERIMENTAL script to completely automate compiling and installing software.
The basic concept is that the user downloads any .tar.gz or .tar.bz2 archive created using autoconf (i.e. most programs), and types
- autodeb archive.tar.gz
The program will be configured, compiled and installed, and a binary .deb package will be created and installed on the system.
Autodeb tries to automatically find and install dependencies (both those needed for compiling, and those needed at runtime), and it lists runtime dependencies as such in the created .deb.
It uses (a modified version of) AutoApt to find development dependencies during 'configure', and CheckInstall to create a .deb binary package.
Contrary to standard AutoApt, autodeb does not just install every file that 'configure' happens to peek at. Instead, it lets 'configure' run and fail, at which point it installs only the last package that 'configure' required, and repeats the process.
Slower (but not really, as a trick is used to reduce the size of the AutoApt database), but avoids installing a lot of useless "false" dependencies (auto-apt always tries to install G77 on my system, just to mention one).
Autodeb can be downloaded at http://ljl.150m.com/autodeb.sh , but PLEASE BE AWARE THAT IT IS VERY EXPERIMENTAL SOFTWARE THAT USES SUPERUSER PRIVILEDGES, AND AS SUCH MIGHT DAMAGE YOUR SYSTEM.
Don't forget to "chmod +x ./autodeb.sh" to make the script executable!
The program I've tested (sort of) autodeb with is XTraceroute, which can be downloaded from http://www.dtek.chalmers.se/~d3august/xt/dl/xtraceroute-0.9.1.tar.gz
Concept
Given a directory containing sourcecode and an autoconf "configure" script, autodeb will
1) Run the 'configure' script under an LD_PRELOAD environment (AutoApt), which tracks all of configure's attempts to access files
2) When 'configure' fails, autodeb looks at the last file that 'configure' tried to access; it asks auto-apt to find the package that files belongs to, installs it, and go back to 1)
3) When 'configure' eventually succeeds, autodeb compiles the sources using 'make'
4) At this point, AutoDeb does a 'make install' under another LD_PRELOAD environment (checkinstall), which tracks all files that are installed to the root filesystem structure, and creates a .deb package of them
5) Now autodeb looks at every executable file contained in the .deb package, and calls 'ldd' on them to determine the shared libraries that they require to run
6) auto-apt is then asked to find which packages those libraries belong to, and autodeb lists these packages as dependencies for the CheckInstall-generated .deb package
Use cases
John doesn't have any experience compiling software from sources, but still he wants to install a program that isn't in any repository. He can run "autodeb tarball" and, with a bit of luck, he'll have the program packaged and installed without doing anything else.
Mary has been using GNU/Linux for a long time, and she's used to the intricacies of autoconf and compiling. However, she's a little busy at the moment and she doesn't have time to manually look for a program's dependencies to compile it. With the help of AutoDeb, she can do something else while AutoDeb builds her program for her.
Known problems and bugs
- The generated .deb package does not have any dependencies listed.
FIXED 'configure' is very slow due to AutoApt crazily looking for packages (while only one search would be really needed)
Add your problems here (or contact me directly at ljlbox@tiscali.it if you can't edit this page)...
Changelog
26 November 2005 Finally implemented runtime dependency installing and listing in the Depends: line of created packages.
25 November 2005 Now creating a "custom" auto-apt database that only lists "-dev" packages, making "configure" much faster; implemented gzip and bzip2 archive extraction; fixed name and version extraction from archive name; various miscellaneous fixes.
16 November 2005 Now using aptitude for everything, which should make the script safer, as the black magic I was using previously to find out what packages to remove at program end is no longer needed.
#!/bin/sh
# Copyright (C) 2005 Lorenzo J. Lucchini <ljlbox@tiscali.it> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
# First thing, we state that the directory where the sources reside is # the same as the argument the user passed to us. We will change this # later if needed, but since there is an "rm -r -f" that depends on # the contents of this variable, we better set it immediately.
SOURCE_DIR="$1"
# This function is called when a signal is sent, or an error occurs.. function Abort {
- echo "Aborting..."
RestoreState exit 1
}
function RestoreAPT {
if [ "${DevDepends}" != "" ]; then
- echo "Removing development dependencies:"
echo "${DevDepends}" echo "" sudo aptitude markauto ${DevDepends} 2>/dev/null
- echo "No dependencies to remove." return
- echo "Removing development dependencies:"
}
# This function is called when the program terminates, normally or abnormally. function RestoreState {
- echo "" sudo mv /usr/lib/auto-apt/auto-apt-installer.orig /usr/lib/auto-apt/auto-apt-installer rm "${DEPENDS_LOG}" rm "${SHORT_DB}" if [ "${SOURCE_DIR}" != "$1" ]; then
- #rm -r -f "${SOURCE_DIR}" true
}
function Usage {
- echo "Usage: ${0} FILE" echo "" echo "FILE can be a .gz archive, a .bz2 archive, a .tar archive, or a directory." echo ""
echo "Report bugs to <ljlbox@tiscali.it>." echo "Include \"autodeb\" in the subject." exit 0
}
if [ ! -e "$1" ]; then
- Usage
fi
# We ask for the sudo password now, so that the user won't be presented with sudo pop-ups # later. The password will only be remembered for a set time (15 minutes on my system), # after which it will be asked again, but hopefully, that time will be more than enough. # The worst that can happen is that the password will be asked again later.
echo "Please type your sudo password now." echo "It will be needed during the process in order to install packages." echo "" echo "If you have a CD-ROM (or other removable media) listed in your" echo "/etc/apt/sources.list, please insert it now." echo "" echo "THIS SOFTWARE IS EXPERIMENTAL." echo "IT MIGHT DAMAGE YOUR SYSTEM." echo "USE AT YOUR OWN RISK!" echo "Type CTRL-D now to exit."
sudo -k; sudo -v
if [ $? -ne 0 ]; then
- echo "Exiting." exit 1
fi
echo ""
# If the argument that was feeded to us is an archive, we extract it to # a temporary directory. If it's a directory, we just use that.
if echo "$1" | grep ".bz2$" >/dev/null; then
SOURCE_DIR=mktemp -d ArchiveName=basename "$1" .tar.bz2 echo "Unpacking bzip2 archive..." bunzip2 "$1" --stdout | tar -x --directory "${SOURCE_DIR}" if [ $? -ne 0 ]; then
- echo "Could not extract ${1}." Abort
elif echo "$1" | grep ".tar.gz$\|.tgz" >/dev/null; then
SOURCE_DIR=mktemp -d if echo "$1" | grep ".tar.gz$" >/dev/null; then
ArchiveName=basename "$1" .tar.gz
ArchiveName=basename "$1" .tgz
- echo "Could not extract ${1}." Abort
elif echo "$1" | grep ".tar$" >/dev/null; then
SOURCE_DIR=mktemp -d ArchiveName=basename "$1" .tar echo "Unpacking tar archive..." cat "$1" | tar -x --directory "${SOURCE_DIR}" if [ $? -ne 0 ]; then
- echo "Could not extract ${1}." Abort
elif [ -d "$1" ]; then
- SOURCE_DIR="$1"
ArchiveName=basename "${SOURCE_DIR}"
else
- Usage
fi
# If the .tar archive created one single sub-directory, use that. # If it didn't, try using the base directory, and hope there is "configure" and # a "Makefile" there.
if [ ls -1 ${SOURCE_DIR} | wc -l == "1" ]; then
- SOURCE_DIR="${SOURCE_DIR}/*"
fi
# Try to guess the package name and version. # (name is what comes before a hypen, version is what comes after)
PackageName=echo "${ArchiveName}" | rev | cut -d "-" -f 2- | rev PackageVersion=echo "${ArchiveName}" | rev | cut -d "-" -f1 | rev
# If there is no version, we invent one.
if [ "${PackageName}" == "${PackageVersion}" ]; then
PackageVersion="1.0"
fi
# We don't really trust our guess, so we ask the user, who can decide whether to accept # our guesses (press Enter) or type a custom name and version. Some basic sanity checks # are performed on the user input.
read -e -p "Package name - press Enter to use '${PackageName}' : " if [ "${REPLY}" != "" ]; then
if [ expr "${REPLY}" : " " -ne 0 ]; then
- echo "Bad package name - must not contain spaces." Abort
PackageName="${REPLY}"
fi
read -e -p "Package version - press Enter to use '${PackageVersion}' : " if [ "${REPLY}" != "" ]; then
if `expr "${REPLY}" : " "` -ne 0 && `expr "${REPLY} : "[0123456789]"` -ne 0; then
- echo "Bad package version - must start with a digit." Abort
PackageVersion="${REPLY}"
fi
# We set some variables that will come useful later.
CUSTOM_DIR="pwd/autodeb-${PackageName}-${PackageVersion}" DEPENDS_LOG=tempfile SHORT_DB=tempfile
# Create the output directory
mkdir "${CUSTOM_DIR}"
if [ ! -d "${CUSTOM_DIR}" -o ! -w "${CUSTOM_DIR}" ]; then
- echo "Could not create output directory ${CUSTOM_DIR}."
echo "Do you have write permissions for pwd?" Abort
fi
# We check that auto-apt and checkinstall are installed, and install them if they aren't.
if [ ! -e /usr/bin/auto-apt ]; then
- echo ""; echo "Installing auto-apt..." sudo aptitude -y install auto-apt if [ $? -ne 0 ]; then
- echo "Installing auto-apt failed." echo "You need to have auto-apt installed for autodeb to work." Abort
sudo auto-apt update 2>/dev/null if [ $? -ne 0 ]; then
- echo "Could not update auto-apt's database." echo "Try running 'auto-apt update' manually." Abort
fi
if [ ! -e /usr/bin/checkinstall ]; then
- echo ""; echo "Installing checkinstall..." sudo aptitude -y install checkinstall if [ $? -ne 0 ]; then
- echo "Installing checkinstall failed." echo "You need to have checkinstall installed for autodeb to work." Abort
fi
# Install build-essential. Besides the fact that this package is needed for compiling # just about anything, this also has the purpose of making aptitude run interactively, # so that if there are any bad problems in the APT database, the user will be notified. # Later, we use the option '--q', which could do bad stuff without telling the user.
echo ""; echo "Initializing package system..."
sudo aptitude install build-essential
if [ $? -ne 0 ]; then
- echo "Could not initialize package system." Abort
fi
echo ""; echo "Trimming auto-apt database..."
auto-apt list | grep "\-dev" | cut -f -10 -d "," | auto-apt -D "${SHORT_DB}" merge
# From now on, we need to clean up stuff (restore auto-apt-installer, remove installed # packages) if we exit prematurely.
trap Abort 1 2 3 4 5 6 7 8 9
# Unfortunately, we must hack into auto-apt to get a sane behavior. # We don't want auto-apt to immediately install every package that it thinks 'configure' # is wanting, because many of those are not real dependencies, but just things that # 'configure' looks for by default, such as the G77 Fortran compiler (and worse). # Also, I have experienced many weird behaviors with auto-apt. # # What we do, instead, is make auto-apt just *list* the packages it would install, # and every time 'configure' fails, we only install *the last package* that was # requested before 'configure' failed. We then re-run 'configure'. Slower, but # hopefully saner. # # Now, in order to be able to do this, we must modify the auto-apt-installer script, # to make it just list packages instead of going on an presenting a GTK interface # asking the user to install them. # We backup the original script, and we'll restore it later after we've finished # using auto-apt.
if [ ! -e /usr/lib/auto-apt/auto-apt-installer.orig ]; then
- sudo mv /usr/lib/auto-apt/auto-apt-installer /usr/lib/auto-apt/auto-apt-installer.orig if [ $? -ne 0 ]; then
- echo "Could not make a backup copy of /usr/lib/auto-apt/auto-apt-installer." Abort
else
- echo "A backup copy of /usr/lib/auto-get/auto-apt-installer already exists, continuing."
fi
sudo sh -c "echo >/usr/lib/auto-apt/auto-apt-installer \"#!/bin/sh\"" sudo sh -c "echo >>/usr/lib/auto-apt/auto-apt-installer \"echo >${DEPENDS_LOG} \\\$3\"" sudo chmod --reference=/usr/lib/auto-apt/auto-apt-installer.orig /usr/lib/auto-apt/auto-apt-installer
cd ${SOURCE_DIR}
if [ ! -e configure ]; then
- echo "No configure script was found." echo "Your program may have to be configured in non-standard ways." Abort
fi
echo ""
DBOption="-D ${SHORT_DB}" while true; do
- echo -n "Trying to configure program... "
auto-apt ${DBOption} run ./configure 1>"${CUSTOM_DIR}/configure.log" 2>"${CUSTOM_DIR}/configure.log" if [ $? -ne 0 ]; then
PreviousDependency="${Dependency}" Dependency=tail -1 ${DEPENDS_LOG} | rev | cut -f1 -d "/" | rev if [ "${Dependency}" == "${PreviousDependency}" ]; then
- if [ "${DBOption}" != "" ]; then
- echo "Package not found, trying with full database... " DBOption="" continue
- echo "Cannot configure. See configure.log." Abort
DevDepends="${DevDepends} ${Dependency}" # We don't want aptitude to prompt the user (as the output is redirected); instead, we want # aptitude to just exit whenever a prompt would be displayed. Using apt-get, this can be done # by means of the "-qq" option, but with aptitude, I needed to resort to the following hack. sudo sh -c "yes 'No' | aptitude -y install ${Dependency} 1>'${CUSTOM_DIR}/apt.log' 2>'${CUSTOM_DIR}/apt.log'" if [ $? -ne 0 ]; then
- echo "Could not install ${Dependency}." echo "APT error. See apt.log." Abort
- if [ "${DBOption}" != "" ]; then
- break
done
echo "successful."
# Now let's compile!
echo ""; echo "Compiling program..."
make 1>"${CUSTOM_DIR}/make.log" 2>"${CUSTOM_DIR}/make.log"
if [ $? -ne 0 ]; then
- echo "Could not compile." echo "Error during 'make', see make.log." Abort
fi
# We now 'make install' using checkinstall to create a .deb package containing # the files that 'make install' creates.
echo ""; echo "Installing program..."
sudo checkinstall --default --pkgname="${PackageName}" --pkgversion="${PackageVersion}" >"${CUSTOM_DIR}/install.log"
if [ $? -ne 0 ]; then
- echo "Could not install." echo "Checkinstall may have encountered an error, see install.log." Abort
fi
echo ""; echo "Finding runtime dependencies..."
for File in dpkg -L ${PackageName}; do
NewLibList=ldd ${File} 2>/dev/null if [ $? -eq 0 ]; then
NewLibList=echo "$NewLibList" | awk ' { print $1 } ' LibList="${LibList} ${NewLibList}"
done
# Prune the list of libraries to remove duplicates, and turn it from the ugly # ldd output format into a clean, tidy list of filenames.
LibList=echo "${LibList}" | sort | uniq | awk ' { print $1 } '
# Now find the packages that correspond to the listed libraries...
echo -n "Finding packages containing dependencies..."
for Lib in ${LibList}; do
LibName=basename ${Lib} NewDependency=auto-apt search ${LibName} | rev | cut -f1 -d "/" | rev # Sometimes auto-apt reports the same dependency more than once, if more than # one file has the same name in a package; hence the "sort | uniq". NewDependency=echo "${NewDependency}" | sort | uniq | tr -s "\n" "|" | rev | cut -b 2- | rev if echo "${NewDependency}" | grep "|" >/dev/null; then
- # This is a multi-package dependency. If one of the packages it lists # is already listed in a single-package dependency, then ignore this # dependency. Redundant=0
for Package in echo ${NewDependency} | tr -s "|" " "; do
if echo "$BinDepends" | grep "[ ^]${Package}[ $]" >/dev/null; then
- Redundant=1
BinDepends="${BinDepends} ${NewDependency}"
- # This is a single-package dependency. Remove any multi-package # dependencies listing the same package as this dependency.
BinDepends=`echo "$BinDepends" | \
sed "s/[ ][ ]+|${NewDependency}[ $]//g" | \ sed "s/[ ]${NewDependency}|[ ]+[ $]//g" | \ sed "s/[ ][ ]+|${NewDependency}|[^ ]+[ $]//g" `
BinDepends="${BinDepends} ${NewDependency}"
- # This is a multi-package dependency. If one of the packages it lists # is already listed in a single-package dependency, then ignore this # dependency. Redundant=0
done
echo ""
# Then remove duplicate packages...
BinDepends=echo "${BinDepends}" | tr " " "\n" | sort | uniq | tr -s "\n" " " BinDepends=echo "${BinDepends}" | tr -s " " | sed "s/^ //" | sed "s/ $//" | sed "s/ /, /g"
echo "Your program was found to depend on the following packages:" echo "${BinDepends}"
# Then store the packages left as runtime dependencies for the user's program.
for DEB_PACKAGE in ${PackageName}_${PackageVersion}*.deb; do
OLD_DIR=pwd mv "${DEB_PACKAGE}" "${CUSTOM_DIR}/" cd "${CUSTOM_DIR}" ar x "${DEB_PACKAGE}" tar zxf "control.tar.gz" TailLineNumber=grep --line-number "^Description:" "control" | cut -f1 -d ":" HeadLineNumber=echo "${TailLineNumber} - 1" | bc NewControl=head -n ${HeadLineNumber} "control"; echo "Depends: ${BinDepends}"; tail -n +${TailLineNumber} "control" echo "${NewControl}" >"control" tar zcf "control.tar.gz" "control" ar rc "${DEB_PACKAGE}" control.tar.gz data.tar.gz # We install the package. At this point, it will probably complain about missing dependencies. sudo dpkg -i "${DEB_PACKAGE}" 1>/dev/null 2>/dev/null # But then we run aptitude, which will install those and mark them as automatic. echo ""; echo -n "Installing the following NEW packages:" sudo aptitude -y install "${PackageName}" | grep "^ " | tail -1 cd "${OLD_DIR}" echo ""
done
echo "" echo "Your program has been succesfully installed, and a .deb package has been created." echo "You can find it inside" echo " ${CUSTOM_DIR}" echo "and in the same directory you will also find the following log files:" echo " apt.log" echo " configure.log" echo " make.log" echo " install.log" echo ""
exit 0
AutoDeb (last edited 2010-01-26 10:09:52 by 93-32-50-153)