61 votes

Comment réduire la duplication dans les champs build-depends d'un fichier .cabal ?

Voici un fichier .cabal :

Name:                myprogram
Version:             0.1
-- blah blah blah
Cabal-version:       >=1.9.2

Executable myprogram
  HS-source-dirs:       src
  Main-is:              Main.hs
  Build-depends:        attoparsec == 0.10.*,
                        base == 4.3.*,
                        -- long long list of packages

Test-Suite test
  HS-source-dirs:       test, src
  Type:                 exitcode-stdio-1.0
  Main-is:              Main.hs
  Build-depends:        attoparsec == 0.10.*,
                        base == 4.3.*,
                        -- long long list of packages
                        QuickCheck == 2.4.*

Y a-t-il un moyen de remplacer la longue liste de paquets dépendants de la construction pour la suite de tests par "les mêmes que pour l'exécutable, plus QuickCheck" ?

Edita: informations sur la version.

  • cabal-dev 0.9
  • cabal-install 0.10.2
  • Bibliothèque Cabal 1.10.2.0
  • GHC 7.0.4
  • Plate-forme Haskell 2011.4.0.0

0 votes

Votre fichier cabal est structuré de manière défavorable, ce qui entraîne beaucoup de recompilation et introduit également la duplication de dépendances que vous mentionnez. Vous pouvez faire en sorte que vos tests/exécutables dépendent de votre bibliothèque. Voir stackoverflow.com/questions/12305970/ . En partant de là, vous pouvez aller autant que possible dans la direction indiquée par @Toxaris dans sa réponse, si cela vous plaît.

1 votes

Il y a plans pour ajouter un common au format .cabal où, par exemple, le champ partagé build-depends peuvent être spécifiés.

34voto

Toxaris Points 3265

NOTE : remplacée par la réponse de phadej qui suggère des strophes communes.


Y a-t-il un moyen de remplacer la longue liste de paquets à construire pour la suite de tests par "les mêmes que pour l'exécutable, plus QuickCheck" ?

Pas à ma connaissance. Cependant, il existe un moyen de ne mentionner que la liste des build-depends une fois, en structurant votre projet en trois objectifs :

  1. une bibliothèque qui contient tout votre code, et qui a besoin de la longue liste build-depends.
  2. un exécutable qui consiste en un seul fichier, et qui dépend de la base et de la bibliothèque du dessus.
  3. une suite de tests qui dépend de la bibliothèque ci-dessus, et des paquets de tests que vous utilisez.

C'est peut-être cette approche que propose la réponse d'indygemma, mais le fichier Cabal qui y est proposé n'y parviendra pas tout à fait, comme le souligne Norman Ramsey dans un commentaire. Voici les points principaux de ce dont vous avez besoin dans un fichier Cabal. Pour un exemple complet qui fonctionne pour moi, vous pouvez regarder à ce dossier de la Cabale .

name: my-program
version: ...

library
  hs-source-dirs: src-lib
  build-depends: base, containers, ...
  exposed-modules: My.Program.Main, ...

executable my-program
  hs-source-dirs: src-exec
  main-is: my-program.hs
  Build-depends: base, my-program

test-suite tests
  type: exitcode-stdio-1.0
  hs-source-dirs: src-test
  main-is: tests.hs
  other-modules: ...
  build-depends: base, my-program, test-framework, ...

Points importants :

  • Il existe trois répertoires sources distincts pour les trois cibles. Ceci est nécessaire pour empêcher GHC de recompiler les fichiers de bibliothèque lors de la construction des autres cibles.

  • Tout le code de l'application se trouve dans la bibliothèque. L'exécutable est juste une enveloppe, comme ceci :

    import My.Program.Main (realMain)
    main = realMain
  • La bibliothèque expose tous les modules qui sont nécessaires pour les tests.

Ce dernier point souligne l'inconvénient de cette approche : Vous finissez par devoir exposer les modules internes. Le principal avantage de cette approche est que vous avez moins de duplication dans le fichier Cabal, et peut-être plus important encore, moins de duplication dans le processus de construction : Le code de la bibliothèque sera construit une seule fois, puis lié à la fois à l'exécutable et à la suite de test.

4 votes

Dans Cabal-1.26 (non encore publié), il sera possible de remplacer la fonction library stanza avec library my-internal-lib qui crée une bibliothèque interne appelée "my-internal-lib" qui n'est visible en interne que par les composants de votre paquetage, et non par les autres utilisateurs. Voir github.com/haskell/cabal/pull/3022

0 votes

D'un autre côté, je pense que l'exposition des internes est acceptable, tant qu'elle porte un nom approprié, tel que Internal o Base .

0 votes

Vous savez, j'ai essayé cette approche, et cela échouait lorsque l'on utilisait le même répertoire pour les bibliothèques et les exécutables. Je pense donc que le fait d'avoir un répertoire séparé est en quelque sorte nécessaire pour que cela fonctionne. L'échec a conduit à un message m'indiquant d'ajouter des dépendances au fichier build-depends section de la bibliothèque, même s'ils étaient là

14voto

Oleg Grenrus Points 1193

Depuis la version 2.2, Cabal supporte les strophes communes, pour dédupliquer informations sur la construction champs : https://cabal.readthedocs.io/en/latest/developing-packages.html#common-stanzas

cabal-version:       2.2
name:                myprogram
version:             0.1
-- blah blah blah

common deps
  build-depends: base ^>= 4.11,
                 -- long long list of packages
  ghc-options: -Wall

library
  import: deps
  exposed-modules: Foo

test-suite tests
  import: deps
  type: exitcode-stdio-1.0
  main-is: Tests.hs
  build-depends: foo

5voto

sjakobi Points 806

Vous pouvez également envisager d'utiliser hpack au lieu d'écrire le fichier .cabal à la main :

Dans le format package.yaml de hpack, vous pouvez spécifier un commun dependencies dont les entrées sont ajoutées à chaque composant build-depends lors de la génération du fichier .cabal.

Par exemple, voir l'application hpack paquet.yaml et le produit hpack.cabal .

Pour commencer à utiliser hpack avec un paquet existant, vous pouvez utiliser hpack-convert qui générera le package.yaml à partir d'un fichier .cabal existant.

Pour créer un nouveau paquet qui utilise hpack, vous pouvez utiliser la fonction de stack simple-hpack comme ça : stack new mypkg simple-hpack .

Si vous utilisez pile pour le développement, vous ne devez pas appeler hpack manuellement pour régénérer le fichier .cabal à partir d'un package.yaml mis à jour - stack le fera automatiquement.

-1voto

Omari Norman Points 190

Il n'y a pas de solution facile :

  • vous pouvez utiliser m4 et spécifier vos dépendances une fois, mais vous devrez alors retraiter votre fichier Cabal à travers m4 chaque fois que vous le modifiez.

  • vous pouvez déplacer le code que vous testez vers une bibliothèque, et ensuite spécifier la bibliothèque dans votre Build-depends pour le test. Cela vous oblige à installer une bibliothèque, ne serait-ce que pour exécuter le test.

  • Vous pouvez simplement ne pas mettre le test dans le fichier cabal du tout. Construisez-le avec ghc --make, qui ajoutera les dépendances. Mais vous perdez alors l'intégration de cabal.

-5voto

indygemma Points 45

Il existe une option bibliothèque pour les fichiers .cabal, ce qui résout votre problème.

name:              myprogram
version:           0.1
-- blah blah blah
cabal-version:     >=1.9.2

library
    build-depends: attoparsec == 0.10.*
                 , base == 4.3.*
                 -- long long list of packages

executable myprogram
    hs-source-dirs: src
    main-is:        Main.hs

test-suite test
    hs-source-dirs: test, src
    type:           exitcode-stdio-1.0
    main-is:        Main.hs
    build-depends:  QuickCheck == 2.4.*

2 votes

Ça ne marche pas pour moi. Lorsque je construis, je reçois un message d'erreur qui commence ainsi Implicit import declaration: Could not find module `Prelude': It is a member of the hidden package `base'.

0 votes

Vous devez toujours dépendre de la base et de tout ce que la Main.hs utilise

3 votes

Cette réponse ne fonctionne pas du tout pour moi. En utilisant la version 1.10 de Cabal, je dois juste écrire tout en triplicata .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X