Skip to content

Add haskell exercises' dependency control. #159

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 27, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 139 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,40 +10,68 @@ Please see the [contributing guide](https://github.com/exercism/x-api/blob/maste

### Development Dependencies

Currently, development of the Haskell track assumes that you are using
GHC 7.8.3 with Haskell Platform installed. The following packages need
to be installed if not using Haskell Platform:
There are currently two distinct ways to set up an environment to colaborate
with the development of the Haskell track:

- Using a global GHC installation.
- Using *Stack*.

The first method is more convenient when you just want to write code without
caring too much about which packages are being used. The second one is better
to track exercise's dependencies and test them against multiple GHC versions.

#### Using a global GHC installation

If you have a recent Haskell Platform installed, probably most of the packages
you need are already installed for you. Otherwise, you can manually install
the missing ones.

The following is the list of all packages used in this repository, just don't
trust this list to be updated.

These packages come installed with GHC:

- array
- base
- containers
- filepath
- directory
- process
- time
- unix

These are already installed in recent version of the Haskell Platform:

- attoparsec
- HUnit
- text
- parallel
- QuickCheck
- random
- split
- stm
- vector

These you will have to add to your instalation:

- lens
- old-locale

##### Installing missing packages

```bash
$ comm -13 \
<(ghc-pkg --global list --simple-output --names-only \
| tr ' ' '\n') \
<(find . -name '*.hs' \
| xargs awk \
'/^import/ {if ($2 == "qualified") {print $3} else {print $2}}' \
| sort -u \
| xargs -n1 ghc-pkg find-module --simple-output --names-only \
| cut -d' ' -f1 \
| sort -u)
HUnit
QuickCheck
attoparsec
parallel
random
regex-base
regex-compat
split
stm
text
vector
$ cabal install lens
```

### Running tests
This will download and installed the package named *lens*.


##### Running tests

All the tests:

```bash
$./_test/check-exercises.hs
$ ./_test/check-exercises.hs
-- accumulate
Cases: 5 Tried: 5 Errors: 0 Failures: 0
[…]
Expand All @@ -68,6 +96,91 @@ Cases: 8 Tried: 8 Errors: 0 Failures: 0
SUCCESS!
```

#### Using Stack

If you have stack installed, you can use it to handle all the dependencies
for you. But first, you need to transform the exercises in *stack projects*.

##### Creating a project for an exercise

The `_test/stackalize` bash script will extract the exercise's dependencies
from `_test/dependencies.txt` and create a *stack project* for you:

```bash
$ _test/stackalize --resolver lts-6.4 exercises/leap
```

This will transform the exercise *leap* in a *stack project* using the
resolver *lts-6.4*. Change it for your favourite [Stackage snapshot](https://www.stackage.org/snapshots).

*You can make you life easier adding _test to your path.*

##### Testing with default settings

To download, install the compiler and packages needed, compile and test an
exercise, run the following commands:

```bash
$ cd exercises/leap
$ stack test
```

##### Testing with advanced options

Testing if it compiles without warnings and pass the tests:

```bash
$ stack clean
$ stack test --pedantic
```

Testing with a specific resolver:

```bash
$ stack test --resolver lts-2.22 # GHC-7.8.4
$ stack test --resolver lts-6.4 # GHC-7.10.3
$ stack test --resolver nightly-2016-06-21 # GHC-8.0.1
$ stack test --resolver nightly # Newest compiler and packages.
```

If you are making major changes to an exercise, it's recommended that
you test it against those three versions of GHC with `--pedantic`, to be sure
it will not fail when your *pull request* is tested on *Travis-CI*.

##### Undoing the stack project

If you need to make a *commit*, you can remove the *stack project* and
change the exercise back to it's previous form:

```bash
$ stackalize --undo exercises/leap
```

This command will intentionally leave the *.stack-work* folder intact,
to preserve the cache in case you decide to test it with *stack* again.

#### The Exercises

Each exercise in the Haskell track is composed of at least two files:

- *name_test.hs* # Both filenames must be all lowecase and any deviations
- *example.hs* # will generate failures in the future.

Optionally, a third file with a stub can also be provided:

- *ModuleName.hs* # This file must be named exactly as the module.

#### Tracking dependencies

We also keep track of all the packages used by the examples and test files in
*_test/dependencies.txt*. If you are implementing a new exercise or changing
the dependencies of a existing one, please don't forget to update it.

At the moment, we may not detect incorrectly specified dependencies in your
*pull requests* automatically. But soon we expect *Travis-CI* be able to
catch it, so please declare in *dependencies.txt* all the packages used by
the examples and tests.

## License

The MIT License (MIT)
Expand Down
59 changes: 59 additions & 0 deletions _test/dependencies
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env bash

programName=`basename "$0"`
dependencies="`dirname ${BASH_SOURCE}`/dependencies.txt"
example="example.hs"
testSuffix="_test.hs"

getDependencies () {
(
for pattern in "$@"; do
grep "${pattern}" "${dependencies}" | cut -d: -f2 | xargs -n1
done
) | sort -u
}

exampleDependencies () {
(
for exercise in "$@"; do
getDependencies "${exercise}/${example}"
done
) | sort -u
}

testDependencies () {
(
for exercise in "$@"; do
getDependencies "${exercise}/${exercise}${testSuffix}"
done
) | sort -u
}

selectedDependencies () {
comm ${commArgs} --output-delimiter " " \
<(exampleDependencies "$@") \
<(testDependencies "$@") | xargs -n1
}

eval set -- `getopt -o "" --long file:,examples,tests,only-in-examples,only-in-tests,examples-and-tests,examples-xor-tests -n ${programName} -- "$@"`

commArgs=""

while true ; do
case "$1" in
--examples ) commArgs="-2" ; shift ;;
--tests ) commArgs="-1" ; shift ;;
--only-in-examples ) commArgs="-23" ; shift ;;
--only-in-tests ) commArgs="-13" ; shift ;;
--examples-and-tests) commArgs="-12" ; shift ;;
--examples-xor-tests) commArgs="-3" ; shift ;;
--file ) case "$2" in
"") shift 2 ;;
* ) dependencies=$2 ; shift 2 ;;
esac ;;
-- ) shift ; break ;;
*) echo "$0: Unexpected error using getopt." ; exit 1 ;;
esac
done

selectedDependencies "$@"
140 changes: 140 additions & 0 deletions _test/dependencies.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
accumulate/example.hs: base
accumulate/accumulate_test.hs: base HUnit
allergies/example.hs: base
allergies/allergies_test.hs: base HUnit
anagram/example.hs: base
anagram/anagram_test.hs: base HUnit
atbash-cipher/example.hs: base split
atbash-cipher/atbash-cipher_test.hs: base HUnit
bank-account/example.hs: base stm
bank-account/bank-account_test.hs: base HUnit
beer-song/example.hs: base
beer-song/beer-song_test.hs: base HUnit
binary/example.hs: base
binary/binary_test.hs: base HUnit
binary-search-tree/example.hs: base
binary-search-tree/binary-search-tree_test.hs: base HUnit
bob/example.hs: base
bob/bob_test.hs: base HUnit
clock/example.hs: base
clock/clock_test.hs: base HUnit
connect/example.hs: base array
connect/connect_test.hs: base HUnit
crypto-square/example.hs: base split
crypto-square/crypto-square_test.hs: base HUnit
custom-set/example.hs: base
custom-set/custom-set_test.hs: base HUnit
difference-of-squares/example.hs: base
difference-of-squares/difference-of-squares_test.hs: base HUnit
etl/example.hs: base containers
etl/etl_test.hs: base HUnit containers
food-chain/example.hs: base array
food-chain/food-chain_test.hs: base HUnit
forth/example.hs: base containers text
forth/forth_test.hs: base HUnit text
gigasecond/example.hs: base time
gigasecond/gigasecond_test.hs: base HUnit old-locale time
go-counting/example.hs: base array containers
go-counting/go-counting_test.hs: base HUnit containers
grade-school/example.hs: base containers
grade-school/grade-school_test.hs: base HUnit
grains/example.hs: base
grains/grains_test.hs: base HUnit
hamming/example.hs: base
hamming/hamming_test.hs: base HUnit
hexadecimal/example.hs: base
hexadecimal/hexadecimal_test.hs: base HUnit
house/example.hs: base
house/house_test.hs: base HUnit
kindergarten-garden/example.hs: base containers split
kindergarten-garden/kindergarten-garden_test.hs: base HUnit
largest-series-product/example.hs: base
largest-series-product/largest-series-product_test.hs: base HUnit
leap/example.hs: base
leap/leap_test.hs: base HUnit
lens-person/example.hs: base lens time
lens-person/lens-person_test.hs: base HUnit time
linked-list/example.hs: base stm
linked-list/linked-list_test.hs: base HUnit
list-ops/example.hs: base
list-ops/list-ops_test.hs: base HUnit
luhn/example.hs: base
luhn/luhn_test.hs: base HUnit
matrix/example.hs: base vector
matrix/matrix_test.hs: base HUnit vector
meetup/example.hs: base time
meetup/meetup_test.hs: base HUnit time
minesweeper/example.hs: base array
minesweeper/minesweeper_test.hs: base HUnit
nth-prime/example.hs: base
nth-prime/nth-prime_test.hs: base HUnit
nucleotide-count/example.hs: base containers
nucleotide-count/nucleotide-count_test.hs: base HUnit containers
ocr-numbers/example.hs: base containers split
ocr-numbers/ocr-numbers_test.hs: base HUnit
octal/example.hs: base
octal/octal_test.hs: base QuickCheck
palindrome-products/example.hs: base
palindrome-products/palindrome-products_test.hs: base HUnit containers
parallel-letter-frequency/example.hs: base containers parallel text
parallel-letter-frequency/parallel-letter-frequency_test.hs: base HUnit containers text
pascals-triangle/example.hs: base
pascals-triangle/pascals-triangle_test.hs: base HUnit
phone-number/example.hs: base
phone-number/phone-number_test.hs: base HUnit
pig-latin/example.hs: base
pig-latin/pig-latin_test.hs: base HUnit
pov/example.hs: base
pov/pov_test.hs: base HUnit
prime-factors/example.hs: base
prime-factors/prime-factors_test.hs: base HUnit
pythagorean-triplet/example.hs: base
pythagorean-triplet/pythagorean-triplet_test.hs: base HUnit
queen-attack/example.hs: base
queen-attack/queen-attack_test.hs: base HUnit
raindrops/example.hs: base
raindrops/raindrops_test.hs: base HUnit
rna-transcription/example.hs: base
rna-transcription/rna-transcription_test.hs: base HUnit
robot-name/example.hs: base random
robot-name/robot-name_test.hs: base HUnit
robot-simulator/example.hs: base
robot-simulator/robot-simulator_test.hs: base HUnit
roman-numerals/example.hs: base
roman-numerals/roman-numerals_test.hs: base HUnit
saddle-points/example.hs: base array containers
saddle-points/saddle-points_test.hs: base HUnit array
say/example.hs: base array
say/say_test.hs: base HUnit
scrabble-score/example.hs: base containers
scrabble-score/scrabble-score_test.hs: base HUnit
secret-handshake/example.hs: base
secret-handshake/secret-handshake_test.hs: base HUnit
series/example.hs: base
series/series_test.hs: base HUnit
sgf-parsing/example.hs: base attoparsec containers text
sgf-parsing/sgf-parsing_test.hs: base HUnit containers text
sieve/example.hs: base vector
sieve/sieve_test.hs: base HUnit
simple-cipher/example.hs: base random
simple-cipher/simple-cipher_test.hs: base HUnit
simple-linked-list/example.hs: base
simple-linked-list/simple-linked-list_test.hs: base HUnit
space-age/example.hs: base
space-age/space-age_test.hs: base HUnit
strain/example.hs: base
strain/strain_test.hs: base HUnit
sublist/example.hs: base
sublist/sublist_test.hs: base HUnit
sum-of-multiples/example.hs: base
sum-of-multiples/sum-of-multiples_test.hs: base HUnit
triangle/example.hs: base containers
triangle/triangle_test.hs: base HUnit
trinary/example.hs: base
trinary/trinary_test.hs: base QuickCheck
word-count/example.hs: base containers split
word-count/word-count_test.hs: base HUnit containers
wordy/example.hs: base attoparsec text
wordy/wordy_test.hs: base HUnit
zipper/example.hs: base
zipper/zipper_test.hs: base HUnit
Loading