dependencies graphs with debtree for drupal

I’ve generate a few graphs with debtree for the drupal modules (10 Feb 2010).

The list of direct dependencies is not very interesting. Dependencies in drupal are mostly flat or the go one or two level deep… You can still spot redundant dependencies like for the module get-node where then dependency on contents could be left implicit by the virtue that imagefield depends indirectly on it.

The list of reverse dependencies (actually, both direct and reverse dependencies are shown in this graph) are equally not very exciting. Since all dependencies are conjunctive (meaning the all dependencies must be satisfied for a package to work), these graph can give a measure of the importance of a package with respect to other modules in the repository.


debtree with a local repository

Date Tags debian

debtree is a fantastic tool to create colorful graphs of package dependencies. One small shortcoming is that the user cannot provide a Packages.gz file directly to be used as repository. Since debtree is based on the excellent apt_pkg library, it is actually not that difficult to convince apt to look in a different location. To change debtree (and apt-get_ default behaviour you just need to create a new apt-get repository and then set the environment variable APT_CONFIG appropriately.

So … imagine you create a repository as :

mkdir -p /tmp/apt/{archives,lists}/partial
cp $yourPackagesfile /tmp/apt/lists
cp $yourstatusfile /tmp/apt/

now you need to create a new apt.conf file that looks like :

APT::Get::List-Cleanup "false";
Dir::Cache /tmp/apt;
Dir::State /tmp/apt;
Dir::State::status /tmp/apt/status;
Dir::Etc::SourceList /tmp/apt/sources.list;

Here I assume that the arch of the Packages list is the same as your host arch. Otherwise you must specify and additional apt parameter like :

APT::Architecture "i386";

Now you’re ready to play with your new repo :

APT_CONFIG=apt.conf apt-get update
APT_CONFIG=apt.conf debtree dpkg

done.

UPDATE

let’s put everything in a bash script ready to use :

$cat debtree.sh 
#!/bin/bash

TMPAPT=$HOME/fakeapt

function init() {
  mkdir -p $TMPAPT/{archives,lists}/partial
  cp $1 $TMPAPT/lists/Packages
  touch $TMPAPT/status

cat > $TMPAPT/apt.conf <<EOF
APT::Architecture "$2";
APT::Get::List-Cleanup "false";
Dir::Cache $TMPAPT;
Dir::State $TMPAPT;
Dir::State::status $TMPAPT/status;
Dir::Etc::SourceList $TMPAPT/sources.list;
EOF

cat > $TMPAPT/sources.list <<EOF
deb file:$TMPAPT/lists/ ./
EOF

  APT_CONFIG=$TMPAPT/apt.conf apt-get update
}

function debtree() {
  APT_CONFIG=$TMPAPT/apt.conf /usr/bin/debtree $@
}

case "$1" in
  init)
    init $3 $2
  ;;
  *)
    debtree $@
  ;;
esac

To use it, first, we need to initialize the fake apt repo, then we can just just debtree normally.

$./debtree.sh init amd64 unstable.packages 
Ign file: ./ Release.gpg
Ign file: ./ Translation-en_US
Ign file: ./ Release
Ign file: ./ Packages
Ign file: ./ Packages
Reading package lists... Done
abate@dev.au:~$./debtree.sh bash
Reading package lists... Done
Building dependency tree... Done
digraph "bash" {
    rankdir=LR;
    node [shape=box];
    "bash" -> "base-files" [color=blue,label="(>= 2.1.12)"];
    "base-files" -> "base-passwd" [color=blue,label="(>= 2.0.3.4)"];
    "base-files" -> "awk" [color=purple,style=bold];
    "awk" -> "Pr_awk" [label="-3-",dir=back,arrowtail=inv,color=green];
    "Pr_awk" [label="...",style=rounded];
    "awk" [shape=octagon];
    "bash" -> "debianutils" [color=blue,label="(>= 2.15)"];
    "debianutils" -> "sensible-utils" [color=blue];
    "bash" -> "dash" [color=purple,style=bold,label="(>= 0.5.5.1-2.2)"];
    "dash" -> "debianutils" [color=blue,label="(>= 2.15)"];
    "dash" -> "dpkg" [color=blue,label="(>= 1.15.0)"];
    "bash" -> "libncurses5" [color=purple,style=bold,label="(>= 5.6+20071006-3)"];
    "libncurses5" -> "libgpm2";
    "bash" -> "bash-completion" [label="(>= 20060301-0)"];
    "bash-completion" -> "bash" [color=blue,label="(>= 3.1dfsg-9)"];
    "bash" [style="setlinewidth(2)"]
    "dpkg" [shape=diamond];
}
I: The following dependencies have been excluded from the graph (skipped):
I: libc6
// Excluded dependencies:
// libc6

gammu x301

The other day I played a bit with gammu on my laptop. The integrated modem is an Ericsson F3507g. There are a lot of useful info about this modem. This is just a resume different info I found on the net.

First you would need to install gammu, that is packaged for debian, so no prob. The to connect to the modem you would need a file .gammurc in you home directory (or in /etc) and the correct permission to talk to the modem. This is how my conf file looks like:

[gammu]
port = /dev/ttyACM0
connection = at19200

To enable the modem, if it is not already enabled at boot, you will have to switch it on then then initialize it.

echo 1 > /sys/devices/platform/thinkpad_acpi/wwan_enable
/usr/sbin/chat -v "" "AT+CFUN=1" "+PACSP0" "AT" "OK" > /dev/ttyACM2 <  /dev/ttyACM2 

And this is it. Gammu is ready to talk to the modem and tell me few info :

zed:~# gammu --identify
Device               : /dev/ttyACM0
Manufacturer         : Ericsson
Model                : unknown (F3507g)
Firmware             : R1B/1
IMEI                 : xxxxxxxxxxxxxxxxxxxx
SIM IMSI             : xxxxxxxxxxxxxxxxxxx

The ref man of gammu is something nice to have at hand …

Unfortunately I’ve realized that the SIM card I’m using is not able to register to the net…

zed:~# gammu --networkinfo
Network state        : registration to network denied
GPRS                 : detached

Digging a bit more, this is can be seen directly talking AT with the modem:

AT+CREG=2
OK
AT+CREG?
+CREG: 2,3

This page details the list of AT commands you can use. The 3 part in +CREG: 2,3 means “Registration denied” … sigh, sime to find a new sim…


analyzing drupal dependencies for fun and profit

After few inspiring talks in the drupal room at fosdem I decided to spend few hours to figure out the module dependency system in drupal.

Drupal has a highly modular design. The core is composed by a set of required modules (dependencies) and a set of optional modules (suggests). All contrib modules declare similar dependencies between each other. All dependencies are conjunctive, that is, in order to install a component all its dependencies must be satisfied. There are no conflict between components, and this implies that a module is always installable. The only implicit conflict is that two versions of the same module cannot be installed at the same time. This makes the module installation algorithm trivial as it is equivalent to a simple visit of the dependency graph (that might have cycles).

There is a nice page on the drupal website explaining the format of the metadata for the next version of drupal .

For example :

name = Tables Filter
description = Provides a filter that converts a [table  ] macro into HTML encoded table.
dependencies[] = filter
package = Input filters
core = 6.x

; Information added by drupal.org packaging script on 2009-09-10
version = "6.x-1.0"
core = "6.x"
project = "tables"
datestamp = "1252563652"

Note the conversion in an intermediate aggregate data below.

In order to analyze all modules’ dependencies I’ve downloaded all available modules for the release 6 of drupal (15th Feb 2010), extracted all the meta data and transform them in something that the tools in dose3 can handle. Downloading all projects archives I’ve also find that there a significant number of archives that cannot be downloaded (403 / 404) and few mistakes in the metadata … I’ll blog about this in the future maybe.

==Numbers and intermediate aggregate modules list== From the file .info in each module archive, I extracted all the relevant data and transformed in a 822 format similar to the one used in debian. There are about 4800 modules in the drupal repository for drupal 6.x.

This is a small snippet representing few drupal core modules and a meta package (that I created from the metadata) to express the core’s dependencies) :

[...]
package: tables
version: 6.x-1.0
depends: filter

package: blogapi
version: 6.15

package: profile
version: 6.15

package: filter
version: 6.15

package: drupal
version: 6.15
depends: system , user , block , node , filter
provides: core = 6.15
suggests: translation , comment , menu , openid , contact , tracker , forum , ping , syslog , help , dblog , search , trigger , poll , update , locale , php , path , taxonomy , color , aggregator , upload , throttle , statistics , blog , book , blogapi , profile
[...]

Since I’m considering only modules for drupal version 6.x, all dependencies for core >= 6.0 , core < 7.0 are left implicit.

Dependency graphs

The result are a set of nice graphs showing for each package their (deep) dependencies. From the global dependency graph, I’ve extracted the “connected” components, that is all modules that are related with each other in some way. This generates 375 sub-graphs. This is the top 10 (WARNING: some of the biggest pdf systematically manage to trash my workstation… handle with care) … and circo didn’t manage to create the pdf for views and taxonomy:

The complete list is here

From these graphs, it seems that apart from a couple of dozen of packages, the rest of the drupal components are loosely connected. I don’t think this is a matter of code sharing but this is more likely because the drupal repository has a plethora of small components with a very special functionality that only depends on the drupal core.

Dist check

Distcheck is a small utility that transforms package dependencies in a propositional logic problem and then uses a sta solver to simulate it’s installation. Since there are no conflicts, it should be always possible to install a package. The only reason for a package to be broken is a missing dependency in the repository. Periodically performing this analysis could prevent the distribution of broken packages.

Conclusions

Periodic generation of aggregate module metadata information.

Dist Check the module repository to avoid releasing a module that is

not installable due to a missing dependency.

Integrate a developer tool to display all dependency of a module

(like debtree, or directly using debtree)

As the system grows it might be necessary to review the dependency

system to include disjunctive dependencies and conflicts between modules. At present this might be not necessary, Adding more expressivity to the dependency system of course will significantly increase the complexity of the installation problem (from polynomial to NP-complete).

I think it is important to spend few words about this last point. It is clear that not all 4800 packages can be installed at the same time. Just think about the filter modules that manipulate user’s submissions. At the moment the only was a site developer has to discover a conflict it to try out the module and check if it did not break anything else on the site. Given the complexity of many drupal site this can be a painful and costly task to perform.

Adding conflicts to the meta data will make modules integration much easier for site developers, and move the burden of finding potential problems to the module developers and to the module installer. As I said before if we include conflicts (that is negation, in logical terms) the problem of installing a new module suddenly become NP-complete. Running a NP complete algorithm on a webserver is of course a bad idea, but using drush offline to run complex install operations, should be completely acceptable as much as it is acceptable to wait for apt-get to install the latest program on debian.

If conflicts are indeed needed it would be fun to have a mod_php_minisat and to implement a small dependency solver in php !


create rpm packages on a debian machine

Date Tags rpm

This gave me a bit of an headache … Why on earth rpmbuild does not simply respect environment variables nor have a command line option to specify the TEMPDIR used to build the package or simply look for a simple configuration file in the local directory ? From command line you can only specify —buildroot . For everything else you must specify a global file called .rpmmacros (not .rpmrc !) and write the new defaults there. This must be either in your home, or in /etc (other other rpm specific paths). Anyway … enough for this rant. This is the hack to create a local build environment :

#!/bin/bash

RPMBUILD=`pwd`/rpmbuild
RPMMACROS="$HOME/.rpmmacros"

if [ -f $RPMMACROS ] ; then
  cp $RPMMACROS $RPMMACROS~
fi

echo "%_topdir  $RPMBUILD" > $RPMMACROS
echo "%_tmppath $RPMBUILD/tmp" >> $RPMMACROS
echo "%_rpmdir `pwd`/RPMS" >> $RPMMACROS
echo "%mkrel(c:) %(echo mdv20010)" >> $RPMMACROS

mkdir -p rpmbuild/{SRPMS,BUILD,RPMS,SPECS,tmp}

The mkrel part is something else that is needed if you want to build a package that resemble a mandriva package … The rpm package in debian does not include this macro by default.

I’m using the rpmbuild part of the rpm package on debian sid (RPM version 4.7.2).