David Madore's WebLog: Clopen source : de la difficulté croissante de compiler des programmes

[Index of all entries / Index de toutes les entréesLatest entries / Dernières entréesXML (RSS 1.0) • Recent comments / Commentaires récents]

↓Entry #2844 [older| permalink|newer] / ↓Entrée #2844 [précédente| permalien|suivante] ↓

(mercredi)

Clopen source : de la difficulté croissante de compiler des programmes

Je vais de nouveau tenter d'écrire un ou plusieurs billets pas trop interminables. On va voir comment ça va marcher.

Ce que je veux dire ici va recouvrir en partie ce que je disais déjà dans ce billet récent mais concerne un aspect un peu différent : la difficulté à compiler des programmes sous Linux. Et ce n'est pas juste que ça devient difficile, c'est aussi que plus grand-monde ne le fait, et les deux aspects forment un cercle vicieux.

👉︎ Quelques explications terminologiques pour les personnes pas trop familières de l'informatique : Quand on [= des humains] écrit un programme, la forme textuelle du programme (ou package) sur laquelle on travaille est appelée le code source, ou de façon un peu abusée, le source (notez le genre masculin). La forme que l'ordinateur est capable d'exécuter est appelée l'exécutable binaire, ou de façon un peu abusée, le binaire. Comme le binaire est illisible et non éditable en pratique par un humain, et qu'il dépend des détails de l'ordinateur sur lequel on va le faire tourner (type de processeur, système d'exploitation, etc.), on préfère souvent distribuer le source et refabriquer le binaire au besoin. L'opération par laquelle on passe du source au binaire est appelée la compilation (bon, c'est simplifié parce qu'il peut y avoir d'autres opérations qui vont avec, par exemple de préparer des fichiers de données annexes, mais c'est l'idée). Conceptuellement, ce n'est pas compliqué, on applique un programme appelé compilateur sur le source et il fabrique le binaire (adapté au processeur et à l'OS souhaités). En pratique, le diable est dans les détails, il y a énormément de bouts de source à compiler dans un ordre bien précis, souvent avec des dépendances compliquées, il faut donner des options très précises au compilateur, et il existe toutes sortes de mécanismes différents servant à lancer la compilation comme il faut.

À titre d'exemple, je compile mes propres Firefox[#] à partir du source. Je ne connais absolument personne d'autre qui fasse ça. Pourtant, je fréquente pas mal de geeks, mais je n'ai jamais rencontré qui que ce soit d'autre que moi qui compile Firefox (régulièrement, ou même de temps en temps pour essayer) et qui ne soit pas dans — ou facilement assimilable à — l'une des deux catégories évidentes : ⓐ développeur pour Mozilla, ou bien ⓑ mainteneur d'une distribution Linux. Absolument personne. Je sais parce que j'ai souvent fait des appels à l'aide suite à des problèmes de compilation de Firefox, et personne ne répond jamais. Tout le monde télécharge simplement son Firefox sous forme de binaire sur le site de Mozilla, ou utilise celui de sa distribution.

[#] La principale raison pour laquelle je le fais est qu'il y a un bug insupportable dans Firefox depuis plus de 20 ans(!) qui fait qu'il transforme des espaces insécables (U+00A0 NO-BREAK SPACE) en espaces normales (U+0020 SPACE) lors du copier-coller dans de nombreuses circonstances. Comme je ne veux pas que ce billet devienne interminable, je ne donne pas plus de détails, mais voyez ce fil Bluesky si vous voulez en savoir un peu plus sur le fait que ce bug a été partiellement mais non complètement corrigé, et pourquoi ils ne veulent pas le corriger complètement. Bref, j'ai un patch très facile qui le corrige en grande partie (mais pas complètement non plus) et je tiens à l'appliquer.

Je ne veux pas trop jouer au vieux con sur l'air de c'était mieux âââvant, mais j'ai connu une époque où c'était assez normal de compiler beaucoup de programmes qu'on utilisait. Les raisons pouvaient varier (programme pas packagé par la distribution, souhait d'appliquer un patch ou de changer les options de compilation ou de configuration ou les d'utiliser des bibliothèques différentes, ou d'avoir une version différente, ou simple question de principe), en tout cas, c'était courant.

Et ce n'est pas juste que c'était courant : c'est le principe d'utiliser des logiciels libres, ou open source, que ce soit possible. C'est la liberté la plus basique derrière le terme de logiciel libre, c'est la base de la notion d'open source, et le problème fondamental avec les libertés qu'on n'utilise pas, c'est qu'elles finissent toujours par s'éroder.

Je ne dis pas que c'était terriblement facile. Quand j'ai commencé à utiliser des Unix, c'était vraiment assez chaotique, généralement il fallait éditer des fichiers d'options de compilation pour dire à quoi ressemblait le système pour lequel on voulait compiler (et parfois on n'avait aucune idée de la réponse à des questions du style les gestionnaires de signaux se comportent-ils façon BSD ou façon System V ?). Puis est venu un jeu d'outils appelé les GNU autotools qui fabriquent un script appelé configure qui détecte tout seul plein de choses sur le système au lieu de demander à l'opérateur humain de répondre à toutes ces questions : très commode quand ça marchait, mais souvent ça ne marchait pas et on ne savait pas comment réparer le problème[#2]. Néanmoins, ils avaient l'avantage d'être assez standards, donc on savait à quoi s'attendre et comment s'en servir.

[#2] Ceci est un phénomène récurrent et courant en informatique : plus quelque chose est automatisé, plus c'est commode quand ça marche, mais compliqué de réparer les choses quand quelque chose ne va pas. Il y a certainement des moyens de réduire la tension entre les deux (par exemple fournir des outils capables de décomposer les étapes, montrer ce qu'ils font à chaque étape, proposer des moyens de modifier le comportement à chaque niveau, etc.), mais ils ne sont pas assez mis en œuvre.

A contrario, distribuer des programmes sous forme binaire était terriblement difficile, quasi impossible, il y a 25–30 ans : Linux était quasiment incompatible avec Linux, chaque distribution avait ses propres idiosyncrasies, rendant la compilation quasiment obligatoire. Soit votre distribution se chargeait de vous donner exactement le bon binaire pour vous, soit il fallait le produire par vos propres moyens à partir du source.

Il me semble qu'il y a eu plusieurs évolutions. Distribuer les binaires est devenu plus facile : les distributions Linux ont fait des efforts pour se rendre au moins minimalement compatibles entre elles, les Unix autres que Linux (et éventuellement les *BSD) ont quasi disparu, l'espace disque et le temps de compilation sont plus abondants ce qui permet plus facilement aux auteurs d'un projet de fournir plein de binaires précompilés ; et aussi, divers systèmes sont apparus (Snap, Flatpak, Docker, etc.), que je trouve absolument abominables mais c'est un autre débat, pour permettre de distribuer un programme en même temps que toutes ses dépendances sous une forme qui est censée « juste marcher ». Peut-être aussi que plus de programmes sont fournis par les distributions.

Toujours est-il que ces évolutions ont diminué le besoin de recompiler à partir du source. Et je crois que les gens qui ne sont ni ⓐ développeurs du projet Machintruc ni ⓑ mainteneurs de celui-ci pour le compte d'une distribution, compilent de plus en plus rarement.

Le principal projet qui me semble faire encore exception, c'est le noyau Linux lui-même. Parce qu'il est particulièrement facile à compiler vu qu'il n'a essentiellement pas de dépendances, et parce que le gain est important vu la myriade d'options qu'on peut régler en le compilant, ou de patchs qu'on peut vouloir sélectionner, si bien qu'il est difficile pour les distributions de fournir une version compilée qui convienne à tout le monde. Mais même le noyau, je pense que beaucoup moins de gens le compilent maintenant qu'il y a 25–30 ans ; et l'apparition de code Rust dedans ne va certainement pas simplifier les choses.

Évidemment, il y a des nuances selon les distributions Linux : Gentoo, par exemple, part du principe qu'on compile (quasiment) tout localement. Mais ce n'est pas la compilation au sens dont je parle ici : je parle de recompiler la version upstream, celle qui est publiée par les auteurs du programme, pas un package source créé par les mainteneurs de la distribution (et qui est garanti fonctionner parce que tout a été fait pour, mais qui n'est pas fondamentalement différent d'un binaire quand il s'agit de bidouiller dans les options). Idem pour Nix[#3], qui utilise l'approche consistant à tout figer au bit près. Tout ça ne règle pas le problème fondamental sous-jacent : pour qu'un logiciel soit véritablement open source, il faut qu'on puisse lire, éditer et recompiler son source (celui distribué par les auteurs du projet, pas une version spéciale adaptée pour la distribution), sans que ce soit impossiblement compliqué ; et ce, même si on n'est pas développeur du projet ou mainteneur d'une distribution, et sans que la distribution ait mâché le travail pour nous.

[#3] Les fans de Nix sont insupportables à venir toujours faire la pub de leur truc : Nix n'est pas une solution au problème que les sources sont difficiles à compiler parce que les dépendances sont devenues impossiblement touffues et parfois contradictoires — Nix est un constat d'échec et une façon de contourner le problème en actant ce constat d'échec, mais qui ne règle rien au problème lui-même.

Et il me semble que plus le temps passe, plus la compilation devient difficile. Ça peut être lié à la multiplicité des dépendances, ou au fait qu'elles soient extrêmement pointilleuses (je veux utiliser la version 1.729.42b(8)-deadbeef de la libfoobar et pas une autre). Ça peut être lié aux langages utilisés. Ça peut être simplement le fait que comme de moins en moins de non-développeurs compilent, les auteurs font de moins en moins d'efforts[#4] (les mainteneurs pour les distributions vont de toute façon tout refaire à leur sauce). Ou peut-être que c'est simplement lié à la démocratisation de Linux et du fait que les utilisateurs n'aient pas envie de compiler des choses (mais ceci ne peut pas vraiment expliquer la diminution absolue du nombre de gens qui le font, seulement une diminution relative).

[#4] À l'inverse, on vous pousse à récupérer un binaire, et tout est fait pour rendre ça facile : on vous dit souvent carrément de faire curl someinstallscript | sh pour, en gros, aller chercher un script en ligne et l'exécuter directement (ce qui est d'ailleurs un cauchemar absolu question sécurité). En tout cas, autrefois, le lien download d'un projet Linux typique pointait vers le source, maintenant il pointe vers le binaire (et pour le source on vous renvoie typiquement juste à une page GitHub sans explications).

La difficulté peut prendre diverses formes. Parfois c'est simplement une absence d'explications sur comment compiler le projet : il est difficile d'y voir là autre chose qu'une attitude du style les développeurs savent comment faire, les mainteneurs des distributions feront leur propre sauce, et les autres n'ont qu'à récupérer un binaire. (Ou si ce n'est pas un manque d'explications sur comment compiler, ça peut être sur la façon d'installer le(s) binaire(s) une fois compilé(s), ou même sur l'endroit où ils se trouvent à la fin de la compilation.) Parfois c'est une absence d'explications sur l'outil utilisé[#5]. Le bon vieux système du ./configure --prefix=/some/where && make && make install avait toutes sortes de problèmes, mais au moins on savait quoi faire parce qu'il était standard au point d'être quasi universel.

[#5] Par exemple, les projets Rust utilisent cargo : fort bien, mais il faut se rappeler que la personne qui cherche à compiler le projet ne connaît peut-être rien de Rust (ou peut-être juste assez pour changer, disons, un message d'erreur ou un comportement trivial), et n'a peut-être aucune idée de comment utiliser cargo ni ne veut apprendre. Pour un outil comme make qui peut servir à n'importe quel langage, il est raisonnable de demander que celui qui veut compiler apprenne à s'en servir, mais si l'outil est spécifique à un langage, ce n'est pas raisonnable vu le nombre de langages de programmation différents qui existent. (Et on ne peut pas simplement renvoyer à la doc, qui est généralement beaucoup trop longue, trop générale, et trop orientée pour les gens connaissant déjà le langage et ses conventions, pour pouvoir servir aux gens qui veulent juste compiler une fois un projet utilisant cet outil. Ou alors l'outil doit prévoir une doc spécifique pour ce cas.)

Parfois c'est une difficulté cachée sous une façade en trompe l'œil on a fourni un script qui permet de tout compiler automagiquement (pour la compilation de Firefox, ce script s'appelle mach), ce qui semble effectivement facile mais se transforme en cauchemar dès que quelque chose ne va pas exactement comme prévu (cf. ma note #2 ci-dessus) ou dès qu'on sort du périmètre exact du script magique qui fait tout.

Certains sont d'ailleurs tellement pinailleurs qu'ils commencent par télécharger la version exacte du compilateur qui va faire marcher la compilation (Firefox est de ce genre : il veut une version tellement précise de CLang et Rust et compagnie que le script magique qui le compile commence par télécharger les binaires de tout ça). On se demande un peu quel est l'intérêt de compiler si en fait la compilation consiste à aller chercher d'abord les binaires pour le compilateur qui est le seul à pouvoir servir ! Le source est ce qui est censé pouvoir permettre de reproduire l'outil qu'on compile ab ovo à partir d'outils standards, pas à partir d'une version unique du compilateur que vous ne pouvez pas reproduire elle-même.

Un problème supplémentaire est que ces outils compliqués ont tendance à devenir de moins en moins reproductibles : ils vont stocker des choses dans votre répertoire personnel ($HOME)[#6], soit des bouts de code téléchargés ou déjà compilés ou carrément installés localement, soit un état global (options de configuration ou de compilation, variables diverses), et comme on ne sait pas où et comment ces choses sont stockées ni ce qui peut l'être, on perd l'aspect reproductible d'une compilation. Là aussi, la documentation peut être particulièrement opaque pour qui n'a pas envie d'apprendre à se servir en détail de l'outil, juste de compiler un projet bien précis.

[#6] La pollution du $HOME est une des choses qui m'énervent particulièrement : autrefois, ça ne se faisait jamais, mais maintenant tout le monde semble trouver normal de faire ses besoins malodorants dans un sous-répertoire de $HOME sans qu'il y ait un avertissement préalable. Comme le mien est distribué entre plusieurs machines et fait l'objet de sauvegardes abondantes, l'espace dedans est particulièrement cher : je n'ai pas envie que la moindre compilation le remplisse de giga-octets de données partiellement compilées. Si je lance la compilation dans /usr/local/src/somepackage-0.42 j'entends bien qu'il se serve de ce répertoire et de ce répertoire seulement pour toute écriture. Quand j'ai un doute, je compile sous le nom d'un utilisateur qui n'a pas le droit d'écrire dans son $HOME, ce qui provoque une erreur s'il cherche à le faire : mais si ça arrive, ça ne me dit quand même pas comment remédier au problème.

Toujours est-il que Linux évolue de plus en plus dans la direction d'Android[#7]. Il y a bien quelques logiciels open source dans les applications Android, mais essayer de fabriquer un .apk binaire à partir du source est une tâche incroyablement difficile, et je ne parle pas de recompiler le système Android lui-même (en gros il vous faudra une machine dédiée à ça, avec exactement la bonne version de tous les outils). Et de toute façon, même si vous y arrivez, vous ne pouvez même pas vraiment vous en servir, parce que vous ne pouvez pas signer l'application avec la clé des distributeurs, donc le système Android, sous le prétexte de sécurité (bidon), ne vous laissera pas installer la version que vous aurez compilée à moins d'effacer totalement la version antérieure (et donc de perdre toutes vos données). Bref, tout est fait pour que vous n'ayez aucune liberté dans l'affaire (cf. cet autre billet).

[#7] Oui, Android est un Linux en interne, mais il n'y ressemble pas du tout, et il n'est pas du tout fait pour vous laisser bidouiller avec, cf. ce que j'écrivais il y a déjà 11 ans.

Je propose d'utiliser le terme clopen source (clopen est un mot-valise entre closed et open, souvent utilisé en topologie) pour parler de ces projets qui sont théoriquement open source, mais en pratique faire quelque chose du source est tellement difficile (soit parce qu'il est difficile à compiler ou à comprendre comment compiler, soit parce qu'il y a d'autres barrières comme les signatures Android dont je viens de parler) que c'est presque complètement théorique.

Sur le papier, l'open source a gagné, beaucoup de projets importants ont maintenant un code source public, mais en pratique, c'est surtout le clopen source qui a gagné : vous avez accès à du code, mais c'est tellement difficile d'en faire quoi que ce soit que personne n'en fait rien, et en plus tout le monde s'en fout. Est-ce que ça valait la peine de se battre pour la liberté des logiciels si c'était pour en arriver là ?

↑Entry #2844 [older| permalink|newer] / ↑Entrée #2844 [précédente| permalien|suivante] ↑

[Index of all entries / Index de toutes les entréesLatest entries / Dernières entréesXML (RSS 1.0) • Recent comments / Commentaires récents]