Vous êtes ici : Accueil / 2009 / Juin / Join the Buildout side, Luke !

Join the Buildout side, Luke !

écrit le 08/04/2010 Par jerome
Le but de ce document n'est pas de vous convaincre d'utiliser (ou ne pas utiliser) buildout. C'est plutôt « si vous avez besoin d'utiliser buildout, voilà comment faire sans tout pourrir » !

Pourquoi utiliser buildout, et pourquoi ne pas l'utiliser ?

Le pour

L'intérêt de buildout, c'est de pouvoir gérer des dépendances de packages (au sens packages Python) en dehors des dépendances Debian. Pourquoi ? Parceque sur un même système, on peut avoir une instance qui a besoin de Plone 3.1.2, une autre qui a besoin de Plone 3.1.4 ... Et quand on met à jour le package zope-plone3, on ne veut pas que ça casse les instances déjà existantes. De plus, y a plein de gugusses qui filent leurs produits sous forme de eggs (packages Python) et ça intéragit très mal avec le système de packaging Debian, hélas (je suis le premier à le regretter - ou peut-être le 2è si on compte Gaël wink)

Un des côtés sympas de buildout, c'est d'avoir un buildout.cfg "tout prêt" (attention : il faut quand même se donner la peine de le faire ; et c'est facile de faire un buildout.cfg tout pourri qui ne marchera pas!), et de pouvoir le passer à un autre développeur pour lui dire "tu dois bosser sur le projet trucmuche ? Bah pour te monter ton environnement de développement, tiens, tu prends ce buildout.cfg et ça va tout te mettre en place".

Le contre

Si vous avez des packages Python qui ne sont pas setuptools-aware (comme PIL, voir plus bas), il faut bidouiller pour que ça marche. Et parfois ça peut très bien ne pas marcher.

Si le buildout.cfg est pourri, ça ne marchera pas, et ça ne sera pas facile de trouver pourquoi si vous ne connaissez pas buildout.

Si vous avez un site qui n'est pas "buildoutisé" et que vous voulez faire un buildout.cfg, ça ne sera pas forcément facile. Il ne faut pas systématiquement essayer de faire rentrer des pièces carrées dans des trous ronds.

Si vous avez un site "buildoutisé" et que vous voulez le mettre sur une autre machine, vous pouvez a priori faire un coup de rsync (ou un tarball) si vous avez utilisé la méthode virtualenv décrite ci-dessous, mais ça n'est pas forcément fait pour (il peut y avoir des effets de bord que je ne connais pas).

Le but de buildout c'est de mettre en place un environnement (une ou plusieurs instances Zope, avec des produits installés dedans) ; pas forcément de permettre de "repacker" un environnement existant pour le "dépacker" quelquepart ailleurs. Autrement dit, pour les projets à la Pilot, où on a un site de dev et qu'on passe ensuite en prod parceque le client a déjà commencé à mettre des contenus dessus, ça ne résout pas le problème de déploiement (pas de manière immédiate en tout cas). Mais je poursuis la réflexion pour savoir ce qu'on peut faire dans ce domaine afin de se simplifier la vie !

Comment utiliser buildout sous Debian

Le problème : buildout vautre comme une vieille loutre bourrée si jamais il y a déjà des packages Python installés avec apt/dpkg (ou tout autre système de packaging).

La solution :

  • ou bien on est sur un système "vierge", avec juste le package "python" (et rien, ou presque, de plus)
  • ou bien on est sur un système qui a déjà plein de packages python-... ; en ce cas là, la meilleure solution c'est d'utiliser virtualenv (on peut aussi faire un chroot, mais c'est un peu plus lourd, et il faut "pourrir" le chroot afin d'y installer la bonne version d'easy_install ...

Petit exemple d'installation d'un plone avec virtualenv et tout le tralala :

  sudo apt-get install python-virtualenv python python-dev python2.4 python2.4-dev
  ENVPATH=/chemin/vers/mon/environnement
  python2.4 /usr/bin/virtualenv --no-site-packages $ENVPATH
  cd $ENVPATH
  # pour modifier l'environnement du shell afin de n'utiliser que notre virtualenv :
  source bin/activate
  # NB 1 : je ne sais pas si Plone a besoin du support jpeg et freetype
  # dans PIL ; mais si jamais c'est le cas, avant de lancer l'installation
  # de PIL, il suffit d'installer les deux packages suivants :
  sudo apt-get install libjpeg62-dev libfreetype6-dev 
  # NB 2 : normalement, on devrait pouvoir faire "bin/easy_install PIL",
  # mais comme PIL n'est pas "setuptools-aware" on doit faire les 2 lignes suivantes,
  # la première pour récupérer le source à l'arrache, la seconde pour corriger le namespace.
  bin/easy_install --find-links http://www.pythonware.com/products/pil/ Imaging
  ( cd lib/python2.4/site-packages/PIL* ; mkdir PIL ; mv *.py *.pyc *.so PIL ; )
  bin/easy_install zc.buildout

Ensuite, on met ce fichier buildout.cfg :

  [buildout]
  parts = plone zope2 instance

  [plone]
  recipe = plone.recipe.plone

  [zope2]
  recipe = plone.recipe.zope2install
  url = ${plone:zope2-url}

  [instance]
  recipe = plone.recipe.zope2instance
  zope2-location = ${zope2:location}
  user = admin:admin
  http-address = 8000
  debug-mode = on
  products = ${plone:products}
  eggs = ${plone:eggs}

Ensuite on prie et on tape :

  bin/buildout
  bin/instance fg # rock and roll

Comment ça marche

Pour faire son buildout à la main, on a juste besoin de deux éléments :

  • un fichier de configuration buildout.cfg (comme celui figurant juste au dessus)
  • un script bootstrap.py , qui fait partie de la distribution de buildout. Ce script est "constant" (il n'y a rien à customiser dedans). Quand on le lance, il crée un répertoire bin dans lequel il place un script exécutable bin/buildout. C'est ce dernier qu'on lance pour "faire" son buildout. Si on modifie son fichier buildout.cfg, il faut relancer bin/buildout ; en revanche pas la peine de relancer le bootstrap.py. L'intérêt du script bootstrap.py, c'est qu'on peut le lancer avec la version de Python qu'on veut, et c'est ensuite cette version qui sera utilisée dans toute la suite des opérations. Par exemple, si on lance /opt/myfunkypython/bin/python2.6-rc12 bootstrap.py, et bien ça sera cette version de Python qui sera utilisée quand on fera bin/buildout.

Note : le fichier bootstrap.py peut être récupéré de plusieurs façons différentes :

  • depuis le SVN de buildout
  • en générant un template via paster
  • ... ou bien, on peut s'en passer si on passe par easy_install, par exemple si on lance easy_install zc.buildout (si on est dans un environnement créé avec virtualenv comme dans l'exemple plus haut, ne pas oublier de faire bin/easy_install zc.buildout) le bin/buildout est créé tout seul. NB : dans l'exemple plus haut, comme on fait bin/easy_install plone.recipe.plone et que plone.recipe.plone dépend de zc.buildout (entre autres), cela se fait tout seul.

Format du buildout.cfg

Le fichier buildout.cfg contient des sections façon "RFC822". Un petit exemple :

  # ceci est un commentaire
  [buildout] # ceci est la section "globale", qui est la seule à être obligatoire
  parts = titi toto productdistros # ceci indique le nom des autres sections à utiliser
  develop = 
      src/MonSuperProduit 
      src/MonAutreProduit # si on met plusieurs trucs, faut mettre soit sur une seule ligne,
      src/UnAutreProduit # soit sur plusieurs, mais alors avec un par ligne ...
      src/ps.graffiti # et la notation "à la mode" c'est sous forme de chemin Java/Python smile
  # "develop" indique les répertoires contenant les sources sur lesquels on est en train de bosser.
  # Par convention, on place le tout dans "src".
  # A priori, il faut aussi mettre une section "eggs" contenant au moins les mêmes choses,
  # et qui pourra être incluse depuis les autres sections.
  eggs = src/MonSuperProduit src/MonAutreProduit src/UnAutreProduit src/ps.graffiti

  [titi] # cette section sera bien prise en compte, vu qu'elle est dans "parts"

  [tutu] # mais pas celle-là, en revanche

  # lorsqu'on doit inclure des produits "old school" (sous la forme de targz qu'on décompresse
  # dans le répertoire Products/ de son instance), on utilise souvent une section de cette forme :
  [productdistros]
  recipe = plone.recipe.distros
  urls = 
      http://www.topcoolproduct.com/download/SuperFoo-1.0.5.tar.gz
      http://www.topcoolproduct.com/download/SuperQuux-2.1.0.tar.gz
  # Les tarballs aux urls indiqués seront décompressés "comme il faut".

Que sont tous ces répertoires ?

On a un répertoire src dans lequel on met les bouts de code sur lesquels on travaille.

On a un répertoire eggs dans le quel buildout va mettre les eggs téléchargés (sous forme de .egg s'ils sont zip-safe, ou en les décompressant sinon).

On a un répertoire develop-eggs , qui est géré par buildout (pas besoin de taper dedans).

On a un répertoire parts, qui contient toutes les choses "générées" par les différentes sections. Par exemple, si on a une section "toto", et bien on aura un répertoire parts/toto, et ainsi de suite. Dans la définition d'une section, si on utilise la variable ${nomdelasection:location} cela se traduit par le répertoire en question.

Questions diverses

Je veux un interpréteur Python qui utilise le même PYTHONPATH que les trucs lancés par buildout, et en particulier pouvoir importer les modules Python correspondant à Zope and co !

On peut utiliser cette recipe :

   [zopepy]
   recipe = zc.recipe.egg
   eggs = ${instance:eggs} # mettre ici le nom de la section correspondant à l'instance zope
   # (généralement c'est "instance", mais le modifier si besoin!)
   interpreter = zopepy
   extra-paths = ${zope2:location}/lib/python # et là le nom de la section correspondant à Zope 2
   # (idem, généralement c'est zope2, mais modifier si on l'a changé!)
   scripts = zopepy

Puis on lance bin/zopepy et c'est parti.

C'est quoi au juste une recipe, précisément ?

Littéralement, c'est une recette. À comprendre au sens de « recette de cuisine » : on indique « je veux un risotto aux champignons pour 4 personnes », et la recipe se débrouille pour faire cuire le riz, les champignons, et tout ce qui va avec, en quantités idoines.

Techniquement, c'est un bout de code Python, généralement logé dans un package bien nommé ; ainsi, dans plone.recipe.plone, se trouve la « recette » permettant d'installer Zope + Plone.

Écrire une recipe est pour l'instant au-dessus de mes compétences, et est donc laissé en exercice au lecteur (quelques informations en anglais sont disponibles sur la toile ; sinon, je vous invite à lire l'excellent blog de Gawel sur lequel vous trouverez quelques informations en français sur le sujet).

Comment j'intègre ça dans mon système pour que les instances se lancent au boot ?

Simple (ou presque) : on fait un ln -s /monchemin/blabla/bin/instance /etc/rc2.d/S90instance (par exemple), vu que le script instance est équivalent à zopectl (et qu'on peut donc lui passer les arguments start/stop).

J'ai des sources dans un SVN ; comment je peux intégrer ça dans mon buildout ?

Si ce sont des sources "eggifiées", il y a un moyen sympa de le faire (via la directive develop) ; sinon j'ai réussi à faire la chose suivante, mais je ne sais pas si c'est The Right Thing :

   [buildout]
   parts = plone zope2 instance svn

   [svn]
   recipe = infrae.subversion
   urls =
       http://sprint.pilotsystems.net/svn/collective.gossip collective.gossip
       http://sprint.pilotsystems.net/svn/collective.graffiti collective.graffiti
       http://tracker.trollfot.org/svn/projects/sd.common/trunk sd.common
   # la recipe infrae.subversion va faire des checkouts des URL ci-dessus,
   # dans les sous-répertoires indiqués ; ainsi, la dernière repository citée - sd.common/trunk -
   # se retrouve dans onrepertoirebuildout/parts/svn/sd.common

   [plone]
   recipe = plone.recipe.plone

   [zope2]
   recipe = plone.recipe.zope2install
   url = ${plone:zope2-url}

   [instance]
   recipe = plone.recipe.zope2instance
   zope2-location = ${zope2:location}
   user = admin:admin
   http-address = 8002
   debug-mode = on
   products = 
        ${plone:products}
   extra-paths =
        ${svn:location}/collective.gossip
        ${svn:location}/collective.graffiti
        ${svn:location}/sd.common
   eggs = 
       ${buildout:eggs} 
       ${plone:eggs}
   zcml = collective.gossip
Voir aussi http://pypi.python.org/pypi/gp.svndevelop/

 

Actions sur le document