<div dir="ltr">I just want to say thank you for writing this tool.<div><br></div><div>I may not agree with your interpretation of the PVP on this particular issue, and will probably only use it on a couple of smaller packages that have almost no imports, but at least putting code out there to help those who do want to work in that style is a very helpful and valuable thing.</div>
<div><br></div><div>-Edward</div></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Fri, Feb 28, 2014 at 3:10 PM, Henning Thielemann <span dir="ltr"><<a href="mailto:schlepptop@henning-thielemann.de" target="_blank">schlepptop@henning-thielemann.de</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">I got distracted by writing a tool that checks consistency of package dependencies and import styles. It's not yet perfect, but usable:<br>
<br>
<a href="https://hackage.haskell.org/package/check-pvp" target="_blank">https://hackage.haskell.org/<u></u>package/check-pvp</a><br>
<br>
<br>
Description:<br>
Check whether the version ranges used in the @Build-Depends@ field<br>
matches the style of module imports<br>
according to the Package Versioning Policy (PVP).<br>
See <<a href="http://www.haskell.org/haskellwiki/Package_versioning_policy" target="_blank">http://www.haskell.org/<u></u>haskellwiki/Package_<u></u>versioning_policy</a>>.<br>
The tool essentially looks for any dependency<br>
like @containers >=0.5 && <0.6@<br>
that allows the addition of identifiers to modules<br>
within the version range.<br>
Then it checks whether all module imports from @containers@<br>
are protected against name clashes<br>
that could be caused by addition of identifiers.<br>
.<br>
You must run the tool in a directory containing a Cabal package.<br>
.<br>
> $ check-pvp<br>
.<br>
This requires that the package is configured,<br>
since only then the association of packages to modules is known.<br>
If you want to run the tool on a non-configured package<br>
you may just check all imports for addition-proof style.<br>
.<br>
> $ check-pvp --include-all<br>
.<br>
It follows a detailed description of the procedure<br>
and the rationale behind it.<br>
.<br>
First the program classifies all dependencies<br>
in the Cabal file of the package.<br>
You can show all classifications with the @--classify-dependencies@ option,<br>
otherwise only problematic dependencies are shown.<br>
.<br>
A dependency like @containers >=0.5.0.3 && <0.5.1@<br>
does not allow changes of the API of @containers@<br>
and thus the program does not check its imports.<br>
Clashing import abbreviations are an exception.<br>
.<br>
The dependency @containers >=0.5.1 && <0.6@<br>
requires more care when importing modules from @containers@<br>
and this is what the program is going to check next.<br>
This is the main purpose of the program!<br>
I warmly recommend this kind of dependency range<br>
since it greatly reduces the work<br>
to keep your package going together with its imported packages.<br>
.<br>
Dependencies like @containers >=0.5@ or @containers >=0.5 && <1@<br>
are always problematic,<br>
since within the specified version ranges identifier can disappear.<br>
There is no import style that protects against removed identifiers.<br>
.<br>
An inclusive upper bound as in @containers >=0.5 && <=0.6@<br>
will also cause a warning, because it is unnecessarily strict.<br>
If you know that @containers-0.6@ works for you,<br>
then @containers-0.6.0.1@ or @containers-0.6.1@ will also work,<br>
depending on your import style.<br>
A special case of inclusive upper bounds are specific versions<br>
like in @containers ==0.6@.<br>
The argument for the warning remains the same.<br>
.<br>
Please note that the check of ranges<br>
is performed entirely on the package description.<br>
The program will not inspect the imported module contents.<br>
E.g. if you depend on @containers >=0.5 && <0.6@<br>
but import in a way that risks name clashes,<br>
then you may just extend the dependency to @containers >=0.5 && <0.6.1@<br>
in order to let the checker fall silent.<br>
If you use the dependency @containers >=0.5 && <0.6.1@<br>
then the checker expects that you have verified<br>
that your package works with all versions of kind @0.5.x@<br>
and the version @0.6.0@.<br>
Other versions would then work, too,<br>
due to the constraints imposed by package versioning policy.<br>
.<br>
Let us now look at imports<br>
that must be protected against identifier additions.<br>
.<br>
The program may complain about a lax import.<br>
This means you have imported like<br>
.<br>
> import Data.Map as Map<br>
.<br>
Additions to @Data.Map@ may clash with other identifiers,<br>
thus you must import either<br>
.<br>
> import qualified Data.Map as Map<br>
.<br>
or<br>
.<br>
> import Data.Map (Map)<br>
.<br>
The program may complain about an open list of constructors as in<br>
.<br>
> import Data.Sequence (ViewL(..))<br>
.<br>
Additions of constructors to @ViewL@ may also conflict with other identifiers.<br>
You must instead import like<br>
.<br>
> import Data.Sequence (ViewL(EmptyL, (:<)))<br>
.<br>
or<br>
.<br>
> import qualified Data.Sequence as Seq<br>
.<br>
The program emits an error on clashing module abbreviations like<br>
.<br>
> import qualified Data.Map.Lazy as Map<br>
> import qualified Data.Map.Strict as Map<br>
.<br>
This error is raised<br>
whenever multiple modules are imported with the same abbreviation,<br>
where at least one module is open for additions.<br>
Our test is overly strict in the sense that it also blames<br>
.<br>
> import qualified Data.Map as Map<br>
> import qualified Data.Map as Map<br>
.<br>
but I think it is good idea to avoid redundant imports anyway.<br>
.<br>
Additionally you can enable a test for hiding imports<br>
with the @--pedantic@ option.<br>
The import<br>
.<br>
> import Data.Map hiding (insert)<br>
.<br>
is not bad in the sense of the PVP,<br>
but this way you depend on the existence of the identifier @insert@<br>
although you do not need it.<br>
If it is removed in a later version of @containers@,<br>
then your import breaks although you did not use the identifier.<br>
.<br>
Finally you can control what items are checked.<br>
First of all you can select the imports that are checked.<br>
Normally the imports are checked that belong to lax dependencies<br>
like @containers >=0.5 && <0.6@.<br>
However this requires the package to be configured<br>
in order to know which import belongs to which dependency.<br>
E.g. @Data.Map@ belongs to @containers@.<br>
You can just check all imports for being addition-proof<br>
using the @--include-all@ option.<br>
Following you can write the options<br>
@--include-import@,<br>
@--exclude-import@,<br>
@--include-dependency@,<br>
@--exclude-dependency@<br>
that allow to additionally check or ignore imports<br>
from certain modules or packages.<br>
These modifiers are applied from left to right.<br>
E.g. @--exclude-import=Prelude@ will accept any import style for @Prelude@<br>
and @--exclude-dependency=foobar@ will ignore the package @foobar@,<br>
say, because it does not conform to the PVP.<br>
.<br>
Secondly, you may ignore certain modules or components of the package<br>
using the options<br>
@--exclude-module@,<br>
@--exclude-library@,<br>
@--exclude-executables@,<br>
@--exclude-testsuites@,<br>
@--exclude-benchmarks@.<br>
E.g. @--exclude-module=Paths_PKG@ will exclude the Paths module<br>
that is generated by Cabal.<br>
I assume that it will always be free of name clashes.<br>
.<br>
Known problems:<br>
.<br>
* The program cannot automatically filter out the @Paths@ module.<br>
.<br>
* The program cannot find and check preprocessed modules.<br>
.<br>
* The program may yield wrong results in the presence of Cabal conditions.<br>
.<br>
If this program proves to be useful<br>
it might eventually be integrated in the @check@ command of @cabal-install@.<br>
See <<a href="https://github.com/haskell/cabal/issues/1703" target="_blank">https://github.com/haskell/<u></u>cabal/issues/1703</a>>.<br>
.<br>
Alternative:<br>
If you want to allow exclusively large version ranges, i.e. @>=x.y && <x.y+1@,<br>
then you may also add the option @-fwarn-missing-import-lists@<br>
to the @GHC-Options@ fields of your Cabal file.<br>
See <<a href="https://ghc.haskell.org/trac/ghc/ticket/4977" target="_blank">https://ghc.haskell.org/trac/<u></u>ghc/ticket/4977</a>>.<br>
Unfortunately there is no GHC warning on clashing module abbreviations.<br>
See <<a href="https://ghc.haskell.org/trac/ghc/ticket/4980" target="_blank">https://ghc.haskell.org/trac/<u></u>ghc/ticket/4980</a>>.<div class="HOEnZb"><div class="h5"><br>
<br>
______________________________<u></u>_________________<br>
Libraries mailing list<br>
<a href="mailto:Libraries@haskell.org" target="_blank">Libraries@haskell.org</a><br>
<a href="http://www.haskell.org/mailman/listinfo/libraries" target="_blank">http://www.haskell.org/<u></u>mailman/listinfo/libraries</a><br>
</div></div></blockquote></div><br></div>