Journal xppq, ou une autre approche de la gestion des arguments de la ligne de commande...

Posté par (page perso) . Licence CC by-sa
Tags : aucun
5
2
juin
2015

xppq est un préprocesseur XML, auparavant nommé expp, qui tourne sur Linux (et probablement sous d'autres UNIX-like), OS X, et Windows, que ce soit sous architecture IA-32, x86-64 ou ARM. xppq est diffusé sous GNU AGPL. Voici les liens correspondants aux précédents journaux dédiés à son prédécesseur :

À cause d'un remaniement de mon serveur, tous les liens donnés dans ces journaux n'existent plus. Mais vous trouverez leurs équivalents sur la page dédiée à xppq, qui se situe à http://q37.info/computing/epeios/tools/xppq/ (en anglais).

xppq est le premier d'une série de logiciels à venir, basés sur le framework Epeios, implémentant un nouveau système de gestion des arguments de la ligne de commande. Comme il s'agit là de la principale nouveauté de xppq (outre son changement de nom), c'est là-dessus que va porter ce journal.

En tout premier lieu, il faut savoir que tous les logiciels basés sur le framework Epeios s'appuient en interne ce qui s'appelle une registry. Cette registry est un arbre dont chaque nœud est identifié par un nom, et qui peut se voir affecter un ou plusieurs attributs. L’accès à l'un de ces nœuds se fait par des expressions à la XPath simplifié (genre Parameters/File[Type="text"]). Les valeurs stockées dans cet arbre ne sont pas typées (concrètement, chaque valeur est stockée comme une chaîne de caractère).

À titre d’exemple, supposons que l'on veuille lancer xppq sur un fichier input.xml pour générer un fichier output.xml. Pour ce faire, le logiciel a besoin d'un certains nombre de paramètres, à savoir :
- la commande à lancer,
- si le contenu généré doit être ou non indenté,
- le namespace à utiliser,
- le nom du fichier d'entrée,
- le nom du fichier de sortie.

Les valeurs de ces paramètres vont être lues par le logiciel dans la registry, dans une sous-section dont la racine à pour nom Parameters, précisément en utilisant respectivement les expressions suivantes:
- Parameters/Command
- Parameters/Indentation
- Parameters/Namespace
- Parameters/Files/Input
- Parameters/Files/Output

Ces paramètres, on va bien entendu les passer via les arguments de la ligne de commande. Cela peut être fait de la manière suivante, en utilisant une option spéciale de la forme --#<path>=<value>, ce qui donne, sachant que path est automatiquement préfixé par Parameters :

xppq --#Command=Process --#Indentation=no --#Namespace=xpp --#Files/Input=input.xml --#Files/Output=output.xml

Sachant que la registry est remplie avec le contenu d'un fichier dit de configuration, on pourra indiquer dans ce fichier les valeurs données pour les trois premiers arguments, qui deviendront de ce fait les valeurs par défaut, sachant qu'on peut utiliser une autre valeur en la redonnant comme argument de la ligne de commande. Bien que l'on puisse également donner la valeur des deux derniers arguments de la même manière, cela ne présente guère d'intérêt, le nom des fichiers d'entrée et de sortie changeant probablement souvent d'un appel à l'autre de cette commande…

Quoi qu'il en soit, passer des paramètres de cette manière n'est pas très confortable et assez éloigné de ce qui se fait habituellement, aussi va-t-on simplifier cela en définissant des raccourcis.

Pour ce faire, on va remplir une section appelée Arguments dans le fichier de configuration déjà mentionné ci-dessus. Celle-ci contiendra une ou plusieurs balise Argument dont la forme générale est la suivante :

<Argument id="..." Description="..." long="..." short="..." Path="..." Value="..." Usage="..."/>

C'est la présence ou l'absence des attributs qui détermine la nature de l'argument, sachant que les attributs id et Description devront toujours être présents.

Le raccourci concernant l'argument correspondant à la commande sera définie de la manière suivante.

<Argument id="ProcessCommand" Description="ProcessCommandDescription" long="process" Value="Process" Usage="[%NamespaceOption%] [%NoIndentationFlag%] [%PreserveFlag%] [%InputArgument% [%OutputArgument%]]"/>

Parce qu’il est absent, l'attribut Path est alors considéré comme ayant la valeur Parameters/Command. L'entrée correspondant dans la registry prendra pour valeur le contenu de l'attribut Value. Le contenu de l'attribut long, précédé par --, identifiera cet argument sur la ligne de commande. Le contenu de Usage sera utilisé pour afficher un message d'aide relatif à cette commande (plus de détails là-dessus ultérieurement). A noter que les valeur entre % sont des identifiants d’arguments (valeur de l'attribut id), arguments qui vont être définis par la suite.

Grâce à cette ligne, au lieu d'avoir à écrire :

xppq --#Command=Process ...

on va pouvoir se contenter d'écrire :

xppq --process ...

Pour gérer l’indentation, on va ajouter la ligne suivante :

<Argument id="IndentationOption" Description="IndentationOptionDescription" long="indent" Path="Indentation"/>

Ce coup-ci, on a un attribut Path, mais pas d'attribut Value. C'est cette particularité qui permet de savoir que l'on a affaire à une définition d'option. L'entrée correspondant dans la registry prendra pour valeur celle définie par l'argument correspondant de la ligne de commande. Ainsi, au lieu d'avoir à écrire :

xppq --#Indentation=(yes|no) ...

on va pouvoir écrire :

xppq --indent=(yes|no) ...

On peut également recourir, à la place d'une option, à un drapeau, en le définissant de la manière suivante :

<Argument id="NoIndentationFlag" Description="NoIndentationFlagDescription" long="no-indent" Path="Indentation" Value="No"/>

Là, on a un attribut Path, mais également un attribut Value, ce qui permet de voir que l'on a affaire à une définition de drapeau. Si, dans le fichier de configuration, on active par défaut l'indentation, pour la désactiver, au lieu d'avoir à écrire :

xppq --#Indentation=no ...

il suffira d'écrire :

xppq --no-indent ...

Pour gérer le namespace, on pourra utiliser la ligne suivante :

<Argument id="NamespaceOption" Description="NamespaceOptionDescription" long="namespace" short="n" Path="Namespace" Label="NamespaceOptionLabel"/>

On voit là un attribut short, que l'on a pas encore utilisé jusqu'à présent. A la place d'avoir à écrire :

xppq --#Namespace=xpp ...

on pourra écrire, grâce à la présence de l'attribut long (notez le double tiret --):

xppq --namespace=xpp ...

ou encore, grâce à la présence de l'attribut short (notez le simple tiret -):

xppq -n=xpp ...

La valeur de l'attribut short, s'il est présent, doit contenir un et un seul caractère.

Reste à définir les arguments qui ne sont ni une commande, ni une option et ni un drapeau. Les deux arguments correspondants à ces critères vont être définis de la manière suivante:

<Argument id="InputArgument" Description="InputArgumentDescription" Path="Files/Input" Label="InputArgumentLabel"/>
<Argument id="OutputArgument" Description="OutputArgumentDescription" Path="Files/Output" Label="OutputArgumentLabel"/>

Remarquez que de telles définitions d'arguments n'ont ni attribut long, ni attribut short.

Comme de tels arguments n'ont pas d'identifiant (-x ou --xxx, d'où l'absence d'attributs long et short dans leur définition), c’est leur position qui permet de les identifier. Pour cela, on va ajouter une section Layouts, dans la section Arguments du fichier de configuration, définie de la manière suivante :

<Layouts>
    <Layout command="Process">
        <Link index="0" Target="InputArgument"/>
        <Link index="1" Target="OutputArgument"/>
    </Layout>
</Layouts>

La valeur de l'attribut command indique quelle commande est concernée par le layout en question, étant entendu que chaque commande aura son propre layout. Dans l’exemple ci-dessus, on retrouve, pour valeur de l'attribut command, la valeur de l'attribut Value de la commande correspondante. Chaque link d'un layout donné devra avoir une valeur d'attribut index unique, et contenant des entiers successifs commençant par 0. Notez que la valeur de l'attribut Target correspond à la valeur de l'attribut id d'un des arguments précédemment définis.

Grâce à l'ensemble des ces définitions, ce qui a été précédemment écrit de la manière suivante :

xppq --#Command=Process --#Indentation=no --#Namespace=xpp --#Files/Input=input.xml --#Files/Output=output.xml

pourra être écrit de la manière suivante :

xppq --process --no-indent -n=xpp input.xml output.xml

Concernant les attributs Description, ils contiennent la chaîne de caractère de la description de l’argument correspondant dont le logiciel va afficher la traduction, telle que définie dans la section dédiée, dans la langue sélectionnée.

L'attribut Usage de la définition de l'argument de la commande contient la chaîne décrivant la syntaxe de cette commande.

Grâce à ces deux attributs, le logiciel est en mesure de générer automatiquement la page d'aide du logiciel.

Ainsi, la commande xppq --help, avec comme fichier de configuration celui à l'adresse http://hg.savannah.gnu.org/hgweb/epeios/file/d8fd9788e0fd/tools/xppq/xppq.xcfg, affichera le message suivant :

xppq --help
    Displays this page.
xppq --version
    Displays the version of the program.
xppq --license
    Displays the program license.
xppq [--process] [-n|--namespace=<ns>] [--no-indent] [--preserve] [<in> [<out>]]
    Process the XML flow.
xppq --encrypt [-n|--namespace=<ns>] [--no-indent] [--preserve] [<in> [<out>]]
    Encrypt the XML flow.

--no-indent ('Parameters/Indentation'='No'):
    No indentation.
--preserve ('Parameters/Preserve'='Yes'):
    Take account of the 'preserve' attribute from 'bloc' tag.
<ns> ('Parameters/Namespace'):
    Namespace to use (instead of 'xpp').
<in> ('Parameters/Input'):
    Input file name (use of standard input if missing).
<out> ('Parameters/Output'):
    Output file name (use of standard output if missing).

ou, en français :

xppq --help
    Affiche cette page.
xppq --version
    Affiche la version du programme.
xppq --license
    Affiche la licence du programme.
xppq [--process] [-n|--namespace=<ns>] [--no-indent] [--preserve] [<in> [<out>]]
    Traite le flux XML.
xppq --encrypt [-n|--namespace=<ns>] [--no-indent] [--preserve] [<in> [<out>]]
    Chiffre le flux XML.

--no-indent ('Parameters/Indentation'='No'):
    Pas d'indentation.
--preserve ('Parameters/Preserve'='Yes'):
    Prise en compte de l'attribut 'preserve' de la balise 'bloc'.
<ns> ('Parameters/Namespace'):
    Espace de nom à utiliser (à la place de 'xpp').
<in> ('Parameters/Input'):
    Nom du fichier d'entrée (utilisation de l'entrée standard si absent).
<out> ('Parameters/Output'):
    Nom du fichier de sortie (utilisation de la sortie standard si absent).

Cette approche me semble plus souple que celle à la getopt (qui était également ma première approche), car il y a beaucoup moins de choses à coder en dur dans le source. Cela a en outre pour conséquence d'offrir à l’utilisateur un moyen simple d'adapter les commandes/options/drapeaux à sa convenance. L'un des buts des logiciels libres, par la mise à disposition des sources, et d’offrir la possibilité à l'utilisateur d'adapter le logiciel à ses besoins. Mais si l'utilisateur peut réaliser certaines de ces adaptations sans avoir à retoucher et à recompiler le code source, je pense que c'est d'autant mieux…

  • # Un truc pas clair

    Posté par . Évalué à 4.

    Salut,

    Si je comprend bien, tu poses xppq en « concurrent » de solutions comme getopt ou autre moyen de facilement gérer des paramètres en ligne de commande.

    Donc, c'est un outil (lib ?) plus à destination d'un développeur que d'un utilisateur, même si de par son design, l'utilisateur a la possibilité aussi de changer des trucs ?

    Et donc, ce fichier de configuration, il doit accompagné le logiciel distribué ? Il se situe où dans l'arborescence du système ? Chez l'utilisateur ou au niveau système ? Et le logiciel, il contient quoi comme type de code pour « utiliser » les paramètres ? Il dépend de ta lib ? C'est une lib ou pas en fait ? xppq est utilisé plutôt ? Comment il fait le lien avec le code du logiciel ? process c'est un exécutable classique ? qui dépend d'une lib xppq pour quand même lire les valeurs des arguments ?

    Bon, tu vois, j'ai pas tout compris, c'est pas clair pour moi :)

    Question bonus : pour moi la différence entre paramètre et argument, c'est que le paramètre c'est ce que tu déclare une fois pour toute en disant « ceci est un paramètre de mon programme » et un argument c'est une instantiation d'un paramètre dans une utilisation particulière : « j'appelle mon programme avec ceci comme argument (sous-entendu pour un paramètre particulier) ».
    C'est comme ça que tu le vois ? J'ai l'impression que non puisque tu appelles « arguments » ce que tu mets dans le fichier de configuration alors que ce ne sont pas des arguments selon la définition que j'en ai… Ton avis ? :)

    • [^] # Re: Un truc pas clair

      Posté par . Évalué à 1.

      Je suis d’accord, est-ce que ça remplace getopt ? J’ai aussi l’impression que ça veut reprendre la gestion des fichiers de conf en donnant une approche à la base de registre

      Bref, c’est flou.

      • [^] # Re: Un truc pas clair

        Posté par (page perso) . Évalué à 1. Dernière modification le 03/06/15 à 17:47.

        Je suis d’accord, est-ce que ça remplace getopt ? J’ai aussi l’impression que ça veut reprendre la gestion des fichiers de conf en donnant une approche à la base de registre…

        En effet, cela ne remplace pas seulement getopt, mais prend effectivement en charge la gestion des fichiers de configuration. C'est dû à l'utilisation du framewok Epeios, avec lequel le point d'entrée du logiciel n'est pas le classique main(...) ou équivalent (qui est en fait défini par le framework), mais une autre fonction, (SCLTOOLMain(...), en fait, pour des application en ligne de commande). Lorsque cette dernière est appelée, les fichiers de configurations sont déjà chargés, et les arguments de la ligne de commande sont déjà traités, la registry ayant été remplie en conséquence.

        Bref, c’est flou.

        Comme écrit précédemment (en fait dans un commentaire plus bas), la rédaction de documentation n'est, hélas, pas mon fort (c'est un métier)…

        Freelance en ingénierie informatique.

    • [^] # Re: Un truc pas clair

      Posté par . Évalué à 2.

      De ce que je comprends, xppq est un transformateur XML, un peu comme xslt. Il utilise une partie de son code pour parser ses arguments.
      Je me demande par contre si "pp" veut dire "pull parser".

      • [^] # Re: Un truc pas clair

        Posté par (page perso) . Évalué à 1.

        De ce que je comprends, xppq est un transformateur XML, un peu comme xslt. Il utilise une partie de son code pour parser ses arguments.

        Je ne dirais pas transformateur, dans la mesure où c'est une toute approche que XSLT, déjà par le fait que tout ce qui est relatif aux directives xpp est directement dans le fichier à traiter, et non pas dans un fichier à part, comme avec XSLT

        Je me demande par contre si "pp" veut dire "pull parser".

        pp a la même signification dans xppq que dans cpp, à savoir PreProcessor. En fait, xppq est conçu pour être à XML ce que cpp est au C/C++.

        De toute manière, je ne savais pas ce qu'est un pull parser, mais, pour ce que j'en ai compris, xppq implémente bien un parser de ce type…

        Freelance en ingénierie informatique.

    • [^] # Re: un seul, vraiment ? :-)

      Posté par (page perso) . Évalué à 3.

      Salut,

      Salut !

      Si je comprend bien, tu poses xppq en « concurrent » de solutions comme getopt ou autre moyen de facilement gérer des paramètres en ligne de commande.

      Pas tout à fait. xppq, c'est juste un préprocesseur XML. J'ai écris ce journal pour signaler que c'est le successeur de feu xppq, et j'en ai profité pour parler de ce nouveau système de gestion des arguments de la ligne de commande, car c'est le premier utilitaire que je publie qui l'implémente. Mais comme tous les autres utilitaires que je publierais seront, à l'instar de expp, basés sur le framework Epeios, ils implémenteront également ce système de gestion des arguments de la ligne de commande. xppq n'est pas plus lié à ce système que ne le seront ces autres utilitaires.

      Ceci dit, j'ai effectivement codé ce système pour remplacer celui que j’avais précédemment codé et qui présentait de très fortes similitudes avec getopt. Dans ce sens, ce système peut effectivement être vu comme un concurrent à getopt.

      Donc, c'est un outil (lib ?) plus à destination d'un développeur que d'un utilisateur, même si de par son design, l'utilisateur a la possibilité aussi de changer des trucs ?

      Non, même pas. Pour l'instant, j'ai juste parlé de ce système pour le faire connaître, et j'en ai parlé en conjonction avec xppq pour mettre en évidence que ce n'est pas juste un concept, mais qu'il en existe bel et bien une implémentation fonctionnelle.

      Actuellement, il n'existe pas de bibliothèque, en tant que telle, dédié à ce système. Ce système n'existe qu'en tant que fonctionnalité du framework Epeios. Mais il est bien entendu envisageable d'isoler le code dédié à ce système pour le mettre à disposition sous forme de bibliothèque indépendante. C'est en partie pour éventuellement susciter ce genre de démarche et pour en juger l'opportunité que ce journal a été écrit.

      Et donc, ce fichier de configuration, il doit accompagné le logiciel distribué ? Il se situe où dans l'arborescence du système ? Chez l'utilisateur ou au niveau système ? Et le logiciel, il contient quoi comme type de code pour « utiliser » les paramètres ? Il dépend de ta lib ? C'est une lib ou pas en fait ? xppq est utilisé plutôt ? Comment il fait le lien avec le code du logiciel ? process c'est un exécutable classique ? qui dépend d'une lib xppq pour quand même lire les valeurs des arguments ?

      Comme écrit plus haut, xppq n'est lié à ce système que parce qu'il est basé sur le framewok Epeios. En l'état, pour mettre en œuvre ce système de gestion des arguments de la ligne de commande, c’est tout le framework qu'il faut mettre en œuvre, ce qui implique une approche du développement en C++ (qui est le langage dans lequel est codé le framework Epeios) en particulier et du développement en général qui sort des sentiers battus.

      Bon, tu vois, j'ai pas tout compris, c'est pas clair pour moi :)

      Ça ne m’étonne pas ; je n'ai jamais été très doué pour écrire de la documentation…

      Question bonus : pour moi la différence entre paramètre et argument, c'est que le paramètre c'est ce que tu déclare une fois pour toute en disant « ceci est un paramètre de mon programme » et un argument c'est une instantiation d'un paramètre dans une utilisation particulière : « j'appelle mon programme avec ceci comme argument (sous-entendu pour un paramètre particulier) ».
      C'est comme ça que tu le vois ? J'ai l'impression que non puisque tu appelles « arguments » ce que tu mets dans le fichier de configuration alors que ce ne sont pas des arguments selon la définition que j'en ai… Ton avis ? :)

      Mon emploi de l'un ou l'autre terme est plus une question de feeling que le fruit d'une véritable réflexion. Je pense que, pour l'idée que je m'en fais, ces deux termes sont assez interchangeables…

      Freelance en ingénierie informatique.

      • [^] # Re: un seul, vraiment ? :-)

        Posté par (page perso) . Évalué à 4. Dernière modification le 04/06/15 à 15:45.

        Dans la mesure ou Epeios est un framework « peu répandu » (pour rester dans l’euphémisme) et que cette manière de lire les paramètres de la ligne de commande nous a bien été vendue dans ce journal, c’est vrai qu’une lib à part pour cet usage me paraîtrait tout à fait bienvenue. En tout cas, je vois personnellement un usage qui m’intéresse d’une telle lib si elle existait alors que je ne vois aucune raison d’utiliser Epeios puisque je ne le connais pas en dehors de cette fonctionnalité.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.