Cabal configurations

benmachine edited this page Mar 18, 2013 · 3 revisions

Imported from Trac wiki; be wary of outdated information or markup mishaps.

Cabal Configurations Progress

On this page I (Thomas Schilling) will document some of the design decisions and my progress on implementing Cabal configurations. If you have any questions ping me on #haskell or send a mail to me or, better, the cabal-devel mailing list. My nickname is "nominolo", my email is at gmail.com.

I am working mostly based on the latest proposal.

.cabal Syntax

Old cabal files still work, but they do not support configurations features. Thus, if you use the new syntax and features, be nice to others and include the following line, so that they get a more useful error messago than "Syntax error". :)

Cabal-version: >= 1.1.7

Here is an example of a .cabal file that uses configurations.

Name: Test1
Version: 0.0.1
Cabal-Version: >= 1.1.7
License: BSD3
-- License-File: LICENSE
Author: Thomas Schilling <notme@nospam.com>
Maintainer: Thomas Schilling <notme@nospam.com>
--Homepage: http://www.example.com/
Synopsis: Test package to test configurations
Description:
        See synopsis.
        .
        Really.
Category: Useless

Flag Debug {
  Description: Enable debug support
  Default:     False
}

Flag NoBuild {
  Description: Inhibit building this package.
  -- defaults to True
}

Library {
  Build-Depends:   base
  Exposed-Modules: Testing.Test1
  Extensions:      CPP

  if flag(debuG) {
    CC-Options: "-DDEBUG"
    GHC-Options: -DDEBUG
  } 

  if flag(NoBuild) {
    Build-Depends: nonexistentpackage
  }
}

Executable test1e {
  Main-is: T1.hs
  Other-modules: Testing.Test1

  if flag(deBug) {
    CC-Options: "-DDEBUG"
    GHC-Options: -DDEBUG
  } 
}

When configuring it with the usual command line, you now get an additional line, showing which flags were chosen:

$ ./Setup.lhs configure
configure: Reading installed packages...
Configuring Test1-0.0.1...
configure: Flags chosen: nobuild=False, debug=False
Setup.lhs: Warning: No license-file field.
configure: Dependency base-any: using base-2.1.1
  [...]

Note that, even though the default for the "nobuild" flag was True, the required dependencies weren't present, so it was forced to False. If you want to force flags to certain values you can do so by giving the --flags or -f flag to configure. Listing the (case-insensitive) name forces it to True, putting a "-" in front of the name forces it to False. For example:

$ ./Setup.lhs configure -fdebug
configure: Reading installed packages...
Configuring Test1-0.0.1...
configure: Flags chosen: nobuild=False, debug=True
Setup.lhs: Warning: No license-file field.
[...]

or

$ ./Setup.lhs configure --flags="-debug nobuild" 
configure: Reading installed packages...
Configuring Test1-0.0.1...
Setup.lhs: At least the following dependencies are missing:
    nonexistentpackage -any
$

Note that if you want to change the configuration of a package, you have to ./Setup.lhs clean first, to make sure everything gets recompiled.

TODO

  • Move the resolved package description into the local build info.
    • Requires changing the workings of the sdist and clean commands.
      • clean should really be just a simple rm -rf ./dist. To get there preprocessed files must never be written outside of ./dist, which requires some changes:
      • capability to resolve a package by just ignoring all conditions.

Progress Log

2007-07-30:

Started refactoring/cleanup branch that will hopefully make Cabal more testable and easier to maintain.

2007-07-29: Configurations are in head

2007-06-24:

Sent all local patches to mailing list for review,

2007-06-14:

Refactorings, also some pretty printing. Interface seems okay now; parseDescription now returns a PreparedPackageDescription? which, given some environmental parameters, can be resolved to an old PackageDescription? (or not, if the dependencies could not be resolved). We probably want a hook for this.

Compatibility mode working.

2007-06-12:

Prototypical implementation up and running. Next step is integrating it with the standart build process. Not sure what the best interface should be.

2007-05-30:

Started modifying the higher levels of the parsing code to incorporate support for conditions. Lot's of cleaning up required. This is when you learn the merits of XML.

2007-05-29:

Not much hacking today; had exam. Parsing and simplifying conditions works.

2007-05-28:

Low-level parsing works. We now have three syntactic categories:

  • Normal properties: fieldname: value
  • If-blocks: 'if' condition '{' ... '}' ['else' '{' ... '}']
  • Sections: sectionname sectionlabel '{' ... '}' where sectionname can currently be only library or executable. Whether or not a label is required is checked at a higher level.

This scheme is backwards compatible. I plan to add a warning when blocks are used but no Cabal-version: > 1.1.7 or equivalent is present.

Discussions

How to make Cabal more maintainable?

2007-07-29, CEST

23:34 <  nominolo> dcoutts: i don't know.  a CabalM monad?
23:34 <     Igloo> It needs to either be something conditionals can talk about or to be something that can be build-depend'ed on, though
23:34 <   dcoutts> nominolo: and what would that monad provide ? (I was thinking of logging at least)
23:35 <  nominolo> dcoutts: and the verbosity level, and the output path, and ...
23:35 <   dcoutts> nominolo: verbosity is easy since we use it everywhere, but some of those other vars are only relevant in some places
23:35 <   dcoutts> with different bits of the code using different environments it's tricky
23:35 <  nominolo> dcoutts: i guess i should really try to create a proposal
23:36 <   dcoutts> nominolo: I'm not sure it needs to be that formal, we should just toss around ideas
23:36 <  nominolo> dcoutts: yes, the issue is that a monad would hide, what's used where
23:36 <   dcoutts> for things that are realistic
23:36 <   dcoutts> eg ignoring ndm's suggestion of throwing the whole code out
23:36 <  nominolo> dcoutts: OTOH, this would make the interface more robust to changes
23:37 <   dcoutts> nominolo: I was thinking of a cabal monad too, to abstract out things like logging and indeed the whole UI
23:37 <  nominolo> dcoutts: well. i'd like to at least mask io, so we can have test cases, by using a fake IO
23:37 <   dcoutts> nominolo: yes!
23:37 <   dcoutts> nominolo: I would get rid of IO in the cabal monad
23:37 <   dcoutts> it'd only have requests to an outside interpreter
23:38 <  nominolo> dcoutts: even better, yep
23:38 <   dcoutts> so it'd be pure internally
23:38 <     Igloo> dcoutts: You need to have some logic looking at IO results, to decide what preprocessors to run, for example
23:38 <     Igloo> But it could certainly be more separate than it is now, yes
23:38 <   dcoutts> it's an interpreter
23:38 <  nominolo> yep, some DSL
23:38 <   dcoutts> it's a conversation between what's running in the cabal monad and an external interpreter
23:39 <   dcoutts> that executes the commands on behalf
23:39 <   dcoutts> and decides security
23:39 <   dcoutts> it could decide to allow anything, or eg it could white-list certain programs
23:40 <   dcoutts> and obviously it could do logging
23:40 <   dcoutts> or it could allow step-by-step debugging
23:40 <   dcoutts> or a gui
23:40 <   dcoutts> by implementing different interpreters of the cabal monad
23:41 <  nominolo> dcoutts: whoa, let's start small ;)
23:41 <  nominolo> dcoutts: i'll start a branch
23:41 <   dcoutts> nominolo: yeah, that's my grand plan, but we could probably do smaller cleanups
23:41 <   dcoutts> nominolo: like Igloo recently cleaned up all the verbose stuff, so it's no longer insane

Dependency Tracking (Parallel Builds, less rebuilding)

23:42 <   dcoutts> the other major thing that would really improve cabal is doing a proper dependency graph, & dep tracking
23:42 <  nominolo> dcoutts: do all compilers support this?
23:42 <   dcoutts> it should not depend on the compiler
23:42 <  nominolo> i know ghc does
23:43 <   dcoutts> cabal can read the .hs files and find the deps
23:43 <   dcoutts> nominolo: since we have to do it for non .hs files too
23:43 <   dcoutts> eg .chs files
23:43 <  nominolo> whoa, that's quite heavy though
23:43 <   dcoutts> not really, we steal the .hs reading code from ghc, or yhc or whereever
23:43 <   dcoutts> ot hmake
23:43 <       ndm> i have written an initial make library
23:43 <       ndm> which i think should meet your needs perfectly
23:43 <   dcoutts> and we use something like ndm's make lib for the dep graph
23:43 <  nominolo> ndm: cool
23:44 <   dcoutts> ndm: and partial rebuilds ?
23:44 <       ndm> dcoutts, the works
23:44 <   dcoutts> excelent
23:44 <       ndm> writing a dependency parser should (in my opinion) be done from scratch, from the haskell report
23:44 <       ndm> i can't imagine its more than a few hours, and does make sure you are 100% outside of other implementations
23:45 <     therp> dcoutts: main/HeaderInfo.hs makes use of a direct call to parseHeader to get the module dependencies. maybe just rip out the header directive from parser/Parser.y?
23:45 <     therp> dcoutts: that way it prevents to parse the whole .hs file (that might contain parse errors) and is still able to get the dep. info
23:46 <   dcoutts> right
23:46 <       ndm> i think it can be done in pure Haskell, without parsec/happy etc, without too much hassle
23:46 <   dcoutts> I think ghc uses happy's partial parser feature
23:46 <   dcoutts> to stop parsing after reading the imports
23:46 <   dcoutts> ndm: probably, though imho it wouldn't matter too much if we used happy
23:47 <   dcoutts> anyway it's not a major problem
23:47 <       ndm> dcoutts, perhaps, i was more thinking for simplicities sake, rather than for minimizing deps
23:47 <   dcoutts> sure
23:47 <       ndm> yeah, its entirely well abstracted, making it easier to change
23:47 <   dcoutts> it has to be pluggable anyway
23:47 <       ndm> anyway, i'm off, i'll pimp my make lib to you at anglohaskell
23:47 <       ndm> bye
23:47 <   dcoutts> since we have to support extracting deps from various file types
23:47 <   dcoutts> .hs .chs etc
23:47 <   dcoutts> ndm: ok cool :-)

Hooks Interface

23:52 <  nominolo> dcoutts: i think the hooks interface should return interpreted instructions, too.  this is the only way i can think of to make it sufficiently future proof
23:52 <   dcoutts> nominolo: yes
23:52 <   dcoutts> not IO
23:53 <  nominolo> and more like hook :: Hookargs -> HookResults
23:53 <   dcoutts> though initially we'd probably need to make it an instance of liftIO
23:53 <   dcoutts> erm I mean MonadIO
23:53 <  nominolo> yep, sure
23:53 <   dcoutts> but the interpreter could always refuse to execute arbitrary IO
23:53 <  nominolo> if we get this running we can safely call it Cabal-2.0

2007-07-31

nominolo + syntaxninja on #haskell-soc

19:29 <  nominolo> we missed our meeting again 
19:30 <  nominolo> but we now have cabal configs in head
19:30 <  nominolo> and dcoutts and Igloo are using them for lots of ghc libraries
19:31 <  nominolo> i'm starting to write the doc now and will try to fix cabal-install
19:32 <  nominolo> meanwhile me and duncan, also persue an idea how to make the cabal codebase more maintainable
19:33 <  nominolo> the basic idea is to have a CabalM monad, that requests some IO actions
19:33 <  nominolo> this can then be run using several ways
19:33 <  nominolo> e.g., single-step debugging or test suites
19:35 <  nominolo> the goal is also to get rid of Setup.hs for standard cases.
19:35 <  nominolo> both changes will allow us to eventually have nicer frontends for cabal
19:36 <  nominolo> oh, and i think the next release of Cabal should be called 1.2 rather than 1.1.7, since this is what we test for when writing Cabal files with newer syntax 
19:37 <  nominolo> also, i think configurations are a rather big change
19:37 < SyntaxNin> whew! sounds great.
19:37 < SyntaxNin> I like the idea of making it 1.2 or even higher.
19:38 < SyntaxNin> 2.0?
19:38 < SyntaxNin> configurations are a big change, as is the Setup change.
19:38 <  nominolo> well, i'd like to do 2.0 after the cleanups
19:38 < SyntaxNin> 2.0 would help prepare people for the magnitude of it :)
19:39 <  nominolo> yes, but we'd like to try to do as many incompatible changes at once in that case
19:39 <  nominolo> ie, other hook interface
19:39 < SyntaxNin> I'm dubious about CabalM, but can trust you guys for it.  I fear it would make using cabal more awkward.
19:39 < SyntaxNin> in my experience w/ the speacial ReadIO and WriteIO monads in Halfs, I've found that they are surprisingly difficult for people to get their heads around.
19:40 <  nominolo> right now we have all IO stuff duplicated
19:40 < SyntaxNin> duplicated?
19:40 <  nominolo> or, rather, what core cabal needs
19:40 <  nominolo> http://code.haskell.org/~nominolo/src/cabal-cleanup/Distribution/Types.hs
19:40 < lambdabot> http://tinyurl.com/242xwu
19:40 <  nominolo> that's my start
19:41 <  nominolo> not sure yet, what we do about the hooks
19:41 <  nominolo> ideally, we don't need them most of the time
19:42 <  nominolo> they can stay in IO, for now
19:42 < SyntaxNin> hm. how pragmatic is this? pragmatism is one of cabal's big strengths.  if the code is cleaner, easier to maintain, that's nice, but not at the cost of easy to use.
19:43 <  nominolo> ok, we'll keep that as a guideline
19:43 <  nominolo> or, directive 
19:43              SyntaxNinja isn't in the position to issue directives.
19:44 < SyntaxNin> consider it advice :)
19:44 <  nominolo> ok :)
19:44 < SyntaxNin> how does IO make the code hard to maintain?
19:45 <  nominolo> it's very hard to test
19:45 < SyntaxNin> was it a serious struggle to add configurations (which is a major change)?
19:45 <  nominolo> we actually have no good test suite
19:45 <  nominolo> well, i don't feel as confident as i should
19:46 < SyntaxNin> the test suite tested most of the major features using IO.
19:46 <  nominolo> it works, mostly, but i'm not so confident we won't have some problems with it
19:46 < SyntaxNin> with configurations specifically?
19:47 < SyntaxNin> how does CabalM help with testing? can you help me understand?
19:48 < SyntaxNin> I don't mean to be injecting doubt, just trying to help explore options.
19:49 <  nominolo> instead of doing the actions, we could record them and put them in a test case
19:49 <  nominolo> or, you could log them somewhere
19:50 < SyntaxNin> and then you'd have some kind of oracle to tell you if the test case is correct?
19:51 <  nominolo> well you get something like [Copy "foo" "bar", RmDir "muh", ..] and that's easy to test
19:51 <  nominolo> *to compare
19:51 < SyntaxNin> so the test case would contain the expected list of actions verses the expected result of the actions?
19:52 <  nominolo> well, it doesn't work if the result is produced by some external program of course
19:53 <  nominolo> the test case would run the CabalM, get some list of actions and compare them, yes
19:54 <  nominolo> we can even embed "unknown" IO actions.  the run-function then decides if it runs them or not
19:55 < SyntaxNin> hm. so if you're comparing against the list of actions, how do you verify that those actions produce the correct result? 
19:57 <  nominolo> hm, if it's simple stuff we just trust the os
19:57 <  nominolo> for external programs we can't
19:57 <  nominolo> though we can build up some fake environments
19:58 <  nominolo> and see if cabal calls with the right parameters
19:58 <  nominolo> we cannot test if external tools actually do TRT
19:59 < SyntaxNin> well, I mean that if you have a list of  actions like [Ghc "foo", Cp "bar", Rm "baz" "booz" "bang"] how do you verify that when you run these actions, the resulting build tree is what you expected (that is, dist/build/foo.o dist/build/docs/bar, etc. end up in the right place)
20:00 < SyntaxNin> the test suite I wrote does lots of builds and checks the build tree to make sure that things are in the right place.  all the .o files, etc.
20:01 <  nominolo> hm, ok, then i have a closer look at that
20:05 < SyntaxNin> cool.
20:11 <  nominolo> ok, it's weird.  it builds moduleTests but doesn't run it
20:11 <  nominolo> at least so it seam
20:11 <  nominolo> *seems
20:12 <  nominolo> and the best thing is: it cleans it right after it built it
20:13 <  nominolo> ah, the right target is check
20:14 <  nominolo> yow, 8 errors
20:16 <  nominolo> actually, it's more
20:19 <  nominolo> http://hpaste.org/1996
20:29 <  nominolo> "dcoutts> ok, it's not IO that's the primary problem"
20:29 <  nominolo> "dcoutts> it's the lack of separation between deciding what to do, and doing it"
20:29 <  nominolo> "dcoutts> because increasingly, deciding what to do is influenced by many factors"
20:39 <  nominolo> SyntaxNinja: i'll put up the logs from #ghc for you in about 1h.  i'll be afk, now
20:41 < SyntaxNin> ok thanks.

nominolo, dcoutts, Igloo on #ghc

No tabs in .cabal files

19:40 <   dcoutts> nominolo: bos doesn't like your tab patch, bos is another tab hater like Igloo :-)
19:40 <       bos> i've nothing against tabs, just against their non-portability
19:40 <   dcoutts> bos: I know ;-)
19:40 <       bos> they're fine upstanding characters, if a bit invisible
19:41 <   dcoutts> I'm not sure it's clear here that it'd break things
19:41 <   dcoutts> and I'm not sure that cabal has rejected tabs in the past
19:41 <       bos> perhaps not, but they always phill me with the phear
19:41 <   dcoutts> aye :-)
19:42 <  nominolo> ok, so disallow them?
19:43 <   dcoutts> nominolo: lemme check something...
19:44 <  nominolo> we do have quite a couple of .cabal files that use them ..
19:44 <   dcoutts> nominolo: and Cabal-1.1.6 allowed them, I just checked
19:44 <   dcoutts> nominolo: so somewhere in recent parser changes they got missed
19:45 <   dcoutts> perhaps it's more important now, I'm not sure
19:45 <   dcoutts> though we've always had layout somewhat
19:45 <   dcoutts> exposed-module:
19:45 <   dcoutts>    Foo -- must be indented
19:47 <  nominolo> before, tabs and spaces didn't matter
19:47 <  nominolo> they were all space
19:47 <  nominolo> and *some* space was all that matters
19:47 <    sorear> I'm not a tab hater.  I'm a guy who hates whoever added :set ts to vi.
19:47 <   dcoutts> hmm
19:47 <   dcoutts> nominolo: I see, and now the amount of space matters
19:47 <  nominolo> dcoutts: exactly
19:48 <  nominolo> well not much
19:48 <  nominolo> it just has to be more than the field name
19:48 <   dcoutts> bos: what would you recommend? we've allowed tabs in the past...
19:48 <  nominolo> but if you mix tabs and spaces, then things can get confusing
19:48 <   dcoutts> bos: for reference the important case is:
19:49 <   dcoutts> library {
19:49 <   dcoutts>   field-foo:
19:49 <   dcoutts>     value
19:49 <   dcoutts> value must be indented more than the field
19:49 <   dcoutts> nominolo: does the field have to be indented? I'm guessing not, but if it is the value has to be indented more
19:51 <       bos> dcoutts: if value must be indented more than field, then tabs will surely break things
19:51 <   dcoutts> bos: so do you think on balance we should start rejecting tabs now?
19:51 <       bos> especially if the indentation is relative, rather than absolute
19:51 <  nominolo> it's relative
19:51 <       bos> dcoutts: well, has that syntax change gone into circulation yet?
19:52 <  nominolo> i could disallow indenting field names
19:52 <  nominolo> but that'll look ugly
19:52 <       bos> the indentation is nice to have.
19:52 <  nominolo> if you have if ... { ... {
19:52 <   dcoutts> nominolo: do you think we could reject tabs in the new library { .. stanzas but keep the original parser for the old style field: value  bits? or is that too complex? I got the feeling the parsers were separate anyway, but I may be wrong.
19:52 <       bos> that's what i'd do.
19:53 <   dcoutts> bos: sorry, what were you referring to?
19:54 <       bos> what you just suggested :-)
19:55 <  nominolo> dcoutts: we could replace tabs by spaces if it's old style
19:55 <   dcoutts> bos: ok :-)
19:55 <  nominolo> at least tabs at the beginning of the line
19:55 <  nominolo> the parsers are not separate
19:55 <   dcoutts> nominolo: aye, if you think that doesn't make the code insane
19:56 <   dcoutts> ah ok
19:56 <   dcoutts> I was just hoping not to break existing .cabal files too much
19:56 <  nominolo> the new old one is just a subset of the new one
19:56 <   dcoutts> for new syntax we can be stricter
19:56 <  nominolo> -old
19:59 <  nominolo> ok, i'll try that

Cabal Test suite failing

20:18 <  nominolo> dcoutts: can you run "make check" on the latest cabal?
20:18              dcoutts tries
20:19 <  nominolo> i get: http://hpaste.org/1996
20:21 <   dcoutts> nominolo: I don't get that far even :-)
20:21 <  nominolo> ah right
20:21 <   dcoutts> nominolo: fails compiling tests/ModuleTest.hs here
20:21 <  nominolo> sorry change import HUnit to import Test.HUnit in ModuleTest.hs
20:21 <  nominolo> sorry. forgot about that
20:22              dcoutts makes again
20:23 <   dcoutts> Cases: 12  Tried: 4  Errors: 0  Failures: 0ghc-pkg: cannot find package HUnitTest-1.0
20:24 <   dcoutts> Cases: 28  Tried: 28  Errors: 5  Failures: 0
20:24 <   dcoutts> several errors
20:24 <  nominolo> better than mine
20:25 <  nominolo> which lets me assume these are not all cabal errors (per se)
20:25 <   dcoutts> in the past I've generally ignored the unit tests 'til just before a release
20:25 <   dcoutts> probably not best practise on my part there :-)
20:25 <  nominolo> tsk tsk tsk

Regarding Cabal refactorings

20:26 <  nominolo> isaac is skeptical about our cabal refactorings
20:27 <  nominolo> he likes the simplicity of IO
20:27 <   dcoutts> hah
20:27 <  nominolo> better: practicality
20:28              dcoutts likes IO soup, it's so opaque and homogeneous
20:28 <   dcoutts> ok, it's not IO that's the primary problem
20:28 <  nominolo> yes, but we need a way to test external tools
20:28 <   dcoutts> it's the lack of separation between deciding what to do, and doing it
20:29 <   dcoutts> because increasingly, deciding what to do is influenced by many factors
20:29 <   dcoutts> and at the moment the decision procedures are all mixed together with each other, and also with the actual doing of stuff
20:30 <   dcoutts> things like, if we're using haddock >= 0.8 then it's ok to use cpp with line pragmas
20:30 <  nominolo> but a CabalM monad would help immediately
20:31 <  nominolo> *would _not_
20:31 <  nominolo> afaics
20:31 <   dcoutts> aye, I've been starting today with some other cleanups
20:31 <   dcoutts> like making greater use of the Program abstraction
20:32 <  nominolo> i don't libe the configure function at all
20:32 <   dcoutts> in theory that should also allow things like my haddock example to be done in a more modular way
20:32 <   dcoutts> nominolo: what is wrong with it do you think? (I'm sure there are many things)
20:32 <  nominolo> it's too long
20:33 <  nominolo> it should be more like.  detectCompiler; getInstalledPackages; checkDependencies; ...
20:34 <  nominolo> and if we have different behaviour for different compilers, we should use a type class
20:34 <   dcoutts> or simply a record of functions
20:35 <   dcoutts> like we do for Program and PreProcessor
20:35 <   dcoutts> nominolo: feel free to have a go at some refactorings
20:35 <  nominolo> yep.  but aren't type classes just syntactic sugar for records of functions?
20:35 <  nominolo> (in a way)
20:36 <  nominolo> dcoutts: please push your changes to the cabal-cleanup repo
20:37 <  nominolo> (or head, if you prefer)
20:37 <   dcoutts> nominolo: oh ok. I just pushed one to head
20:37 <   dcoutts> extracting the haddock code into it's own module
20:37 <  nominolo> yes, good move :)
20:38 <   dcoutts> nominolo: I'm off now for a few hours, I might push the Program abstraction cleanups later this evening
20:38 <  nominolo> ok
20:38 <   dcoutts> nominolo: but yes, good idea to have a -cleanups staging area repo