AutoDeb

Differences between revisions 17 and 18
Revision 17 as of 2005-11-27 22:08:24
Size: 4312
Editor: 62-101-126-215
Comment:
Revision 18 as of 2005-11-27 22:28:56
Size: 19648
Editor: 62-101-126-215
Comment:
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 {

}

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

    else 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

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

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

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

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 fi
    else 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 (last edited 2010-01-26 10:09:52 by 93-32-50-153)