Skip to content

Commit e1f642d

Browse files
authored
Add contributing instructions and scripts (#11)
1 parent 1204f4a commit e1f642d

File tree

9 files changed

+578
-121
lines changed

9 files changed

+578
-121
lines changed

.gitconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[alias]
2+
root = rev-parse --show-toplevel
3+
hello = "!$(git rev-parse --show-toplevel)/scripts/hello"
4+
fmt = "!$(git rev-parse --show-toplevel)/scripts/fmt"
5+
lint = "!$(git rev-parse --show-toplevel)/scripts/lint"
6+
7+
build = "!$(git rev-parse --show-toplevel)/scripts/build"
8+
doc = "!$(git rev-parse --show-toplevel)/scripts/build --haddock"
9+
watch = "!$(git rev-parse --show-toplevel)/scripts/build --file-watch"

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ stack.yaml.lock
44
*~
55
*.cabal
66

7+
haddock-out/
8+
79
runtests.log
810
regression-tests/out
911

CONTRIBUTING.md

Lines changed: 287 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
1-
# Contributing Guidelines
1+
# Contributing
2+
3+
## Table of Contents
4+
5+
<!-- vim-markdown-toc GFM -->
6+
7+
* [Contributing Guidelines](#contributing-guidelines)
8+
* [Rules](#rules)
9+
* [Recommended Git Workflow](#recommended-git-workflow)
10+
* [Development Environment Setup](#development-environment-setup)
11+
* [Build Toolchain Setup](#build-toolchain-setup)
12+
* [Development Tools Setup (optional)](#development-tools-setup-optional)
13+
* [Git Alias Setup (optional)](#git-alias-setup-optional)
14+
* [Developing `sslc`](#developing-sslc)
15+
* [Building sslc](#building-sslc)
16+
* [Running sslc](#running-sslc)
17+
* [Building Code Documentation](#building-code-documentation)
18+
* [Adding Test Suites](#adding-test-suites)
19+
* [Adding Regression Tests](#adding-regression-tests)
20+
* [Adding New Unit Tests](#adding-new-unit-tests)
21+
* [Linting and Formatting](#linting-and-formatting)
22+
23+
<!-- vim-markdown-toc -->
24+
25+
## Contributing Guidelines
226

327
These guidelines are here to ensure that we maintain a clean, linear Git
428
history. All contributions should be made in the form of pull requests (PRs).
@@ -8,7 +32,7 @@ For quick reference about Git commands and concepts, see John's
832

933
[j-hui-yagg]: https://j-hui.com/pages/yagg
1034

11-
## Rules
35+
### Rules
1236

1337
- Collaborators all have write access, so be mindful of what others are working
1438
on.
@@ -65,7 +89,7 @@ For quick reference about Git commands and concepts, see John's
6589
[clang-format]: https://clang.llvm.org/docs/ClangFormat.html
6690
[clang-tidy]: https://clang.llvm.org/extra/clang-tidy/
6791

68-
## Recommended workflow
92+
### Recommended Git Workflow
6993

7094
If you've never made a PR before, here's a basic workflow to follow:
7195

@@ -116,3 +140,263 @@ Here's a diagram to help you illustrate where everything should take place:
116140
(5) git add ...
117141
git commit ...
118142
```
143+
144+
## Development Environment Setup
145+
146+
The sslang compiler, sslc, is developed using the [Haskell programming language][haskell], and developed using the following tools:
147+
148+
- [GHC][ghc]: haskell compiler
149+
- [Cabal][cabal]: build system
150+
- [Stack][stack]: project manager
151+
- [Haddock][haddock]: code documentation generator (packaged with Stack)
152+
- [HLS][hls]: [language server][lsp] (optional)
153+
- [Brittany][brittany]: formatter (optional)
154+
- [Hlint][hlint]: linter (optional)
155+
156+
This section will guide you through setting up your development environnment with these tools. We assume that development will take place in a UNIX-like environment (i.e., macOS, WSL, or some kind of Linux distro). Development in Windows is probably possible but unsupported.
157+
158+
[haskell]: https://www.haskell.org/
159+
[ghc]: https://www.haskell.org/ghc/
160+
[cabal]: https://www.haskell.org/cabal/
161+
[stack]: https://docs.haskellstack.org/en/stable/GUIDE/
162+
[haddock]: https://www.haskell.org/haddock/doc/html/index.html
163+
[hls]: https://haskell-language-server.readthedocs.io/en/latest/
164+
[hlint]: https://hackage.haskell.org/package/hlint
165+
[brittany]: https://hackage.haskell.org/package/brittany
166+
[lsp]: https://langserver.org/
167+
168+
### Build Toolchain Setup
169+
170+
You can easily setup most of sslang's project dependencies using [GHCup][ghcup]<sup>[1](#why-ghcup)</sup>, Haskell's toolchain manager. To do so, run the following command:
171+
172+
```shell
173+
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | \
174+
BOOTSTRAP_HASKELL_INSTALL_STACK=1 \
175+
sh
176+
```
177+
178+
This will run a short but interactive script that installs GHC, Cabal, and Stack; make sure to let it know where it should add the `PATH` variable. GHCup will also ask if you would like to install HLS, which you may use to extend your LSP-compatible editor with IDE features (optional).
179+
180+
The GHCup setup script may also detect that dependencies are missing, and ask you to install them; make sure to do so before proceeding. For instance, if you are running Ubuntu 20.10:
181+
182+
```shell
183+
sudo apt install build-essential curl libffi-dev libffi8ubuntu1 libgmp-dev libgmp10 libncurses-dev libncurses5 libtinfo5
184+
```
185+
186+
Package names differ depending on your distro and version, so make sure to read the suggestion.
187+
188+
<a name="why-ghcup">Footnote 1</a>: _While you can also directly install Stack and use that to manage GHC versions, GHCup is more specialized toward coordinating versioning for just the core components of the toolchain, i.e., GHC, Cabal, Stack, and HLS. You can read more about its rationale [here](https://www.haskell.org/ghcup/about/#faq)._
189+
190+
[ghcup]: https://www.haskell.org/ghcup/
191+
192+
### Development Tools Setup (optional)
193+
194+
If you are helping develop sslc, you may find it helpful to have [HLint][hlint] and [Brittany][brittany] available. You can install these with Stack:
195+
196+
```shell
197+
stack install hlint
198+
stack install brittany
199+
```
200+
201+
### Git Alias Setup (optional)
202+
203+
Convenience scripts are provided under the [`scripts`](./scripts/) subdirectory, to help lint, format, and build this respoitory's code. As long as your current working directory is within this repo, you may invoke these scripts directly.
204+
205+
These scripts may be added as Git aliases for even easier access (e.g., to lint your code, just run `git lint`). They are defined in [`.gitconfig`](./.gitconfig), and can be set up by running the following command inside of this repo:
206+
207+
```shell
208+
git config --local include.path ../.gitconfig
209+
```
210+
211+
Though these convenience aliases are optional, they help outline a recommended command-line workflow that you may wish to follow.
212+
213+
[convenience-aliases]: #git-alias-setup-optional
214+
215+
## Developing `sslc`
216+
217+
### Building sslc
218+
219+
You can build sslc by running:
220+
221+
```shell
222+
stack build
223+
```
224+
225+
By default, Stack does not link in the test driver, and needs to recompile everything if you later decide to run tests. To work around this behavior, you can ask Stack to link in tests without running them:
226+
227+
```shell
228+
stack build --test --no-run-tests
229+
git build # equivalent convenience alias
230+
```
231+
232+
You may also start a build server to continuously watch your filesystem and build as soon as it detects changes, eliminating the need to later run `build` manually:
233+
234+
```shell
235+
git build --test --no-run-tests --file-watch
236+
git watch # equivalent convenience alias
237+
```
238+
239+
You can also continuously build code documentation, though that takes considerably longer, and is not recommended unless you are actively working on documentation:
240+
241+
```shell
242+
git build --test --no-run-tests --file-watch --haddock
243+
git watch --haddock # equivalent convenience alias
244+
```
245+
246+
### Running sslc
247+
248+
You may run sslc using:
249+
250+
```shell
251+
stack exec sslc <args..>
252+
```
253+
254+
You can optionally install `sslc` to `~/.local/bin/`, so that you can invoke it directly (as long as `~/.local/bin/` is in your `PATH`):
255+
256+
```shell
257+
stack install
258+
sslc <args..>
259+
```
260+
261+
All existing tests should be passing before merging a PR, and where appropriate, new tests should be added to demonstrate functionality and correctness of your code. Tests may be run using:
262+
263+
```shell
264+
stack test
265+
```
266+
267+
### Building Code Documentation
268+
269+
Code documentation for this compiler is generated using [Haddock][haddock]. You can build the documentation by running:
270+
271+
```shell
272+
stack haddock
273+
```
274+
275+
This generates the documentation in `haddock-out/`, in the form of a navigable static website (similar to what is found on [Hackage](https://hackage.haskell.org/)). You may view the HTML files in there using your browser. You can also ask that Haddock open your browser automatically:
276+
277+
```shell
278+
stack haddock --open
279+
```
280+
281+
The langauge reference manual is maintained separately in the [`doc/`](doc) folder; see build instructions there.
282+
283+
### Adding Test Suites
284+
285+
All existing tests should be passing before merging a PR, and where appropriate, new tests should be added to demonstrate functionality and correctness of your code. Tests may be run using:
286+
287+
```shell
288+
stack test
289+
```
290+
291+
New test suites can be declared by adding items to the `tests` section in [`package.yaml`](package.yaml). For instance, the scanner test is declared as:
292+
293+
```yaml
294+
tests:
295+
scanner-test: # Name of test suite
296+
main: Spec.hs # Name of test entry point module, in source-dirs
297+
source-dirs:
298+
- test/scanner # Source directories to include in test target
299+
dependencies:
300+
- sslang # Allows us to import modules from sslang
301+
- hspec # Used for managing unit tests; see below
302+
# Other tests...
303+
```
304+
305+
To run an individual test, you may specify `sslang:<test-name>` as a parameter to `stack test`. For instance, to run the scanner test:
306+
307+
```shell
308+
stack test sslang:scanner-test
309+
```
310+
311+
By convention, the `main` module of tests for Haskell projects is typically named `Spec.hs`.
312+
313+
### Adding Regression Tests
314+
315+
Test with, e.g.,
316+
317+
````shell
318+
cd regression-tests && ./runtests.sh
319+
````
320+
321+
TODO: Stephen to write more about `runtests.sh`.
322+
323+
### Adding New Unit Tests
324+
325+
Unit tests provide access to Haskell modules in a way that is more direct than specifying an additional `--dump` option to the executable CLI, and more persistent than using the GHCi REPL. Note, however, that unit tests do not supplant regression tests; code should not be considered tested until it is covered by some regression test. Rather, unit tests provide a way to run your code before you fully integrate it with the rest of the compiler. They should be used throughout the development process to directly test and document the isolated behavior of specific compiler components.
326+
327+
This project manages unit tests using [Hspec][hspec], which provides a basic test driver interface and a plugin for [automatic test discovery][hspec-discover]. For example, the `scanner-test` example declares an Hspec test. The directory structure under `test/scanner` might look like this:
328+
329+
```
330+
test/
331+
|_ scanner/
332+
|_ Spec.hs # main
333+
|_ Tests/
334+
|_ ScanBlockSpec.hs
335+
|_ ScanCommentSpec.hs
336+
| # etc.
337+
```
338+
339+
`test/scanner/Spec.hs` consists of only the following, which tells the `hspec-discover` plugin to look for tests in all subdirectories:
340+
341+
```haskell
342+
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
343+
```
344+
345+
Tests must always end in `Spec.hs` to be included by `hspec-discover`. For instance, `ScanCommentSpec.hs` might look like:
346+
347+
```haskell
348+
module Tests.ScanCommentsSpec where
349+
import Test.Hspec (Spec(..), it, shouldBe)
350+
351+
-- Imports from sslang:
352+
import Front.Scanner (scanTokenTypes)
353+
import Front.Token (TokenType(..))
354+
355+
spec :: Spec
356+
spec = do
357+
it "ignores single-line comments" $ do
358+
scanTokenTypes "// no" `shouldBe` Right []
359+
360+
it "scans tokens before single-line comments" $ do
361+
scanTokenTypes "42 // no" `shouldBe` Right [TInteger 42]
362+
scanTokenTypes "24// no" `shouldBe` Right [TInteger 24]
363+
```
364+
365+
The entry point of each test is the `spec :: Spec` function, which allows individual test cases to be specified in a monadic context. The `it` combinator should be used to provide a succinct description of the behavior in prose (which is reported by the test driver on success), while `shouldBe` tests that its left operand (actual) is equal to its right operand (expected) using their `Eq` instances. If a test fails, Hspec reports the expected and actual outputs using their `Show` instances, and highlights any differences.
366+
367+
You can also run individual test modules or test cases. For instance, to run only the test cases in `ScanCommentsSpec`, run:
368+
369+
```shell
370+
stack test sslang:scanner-test --ta '--match "/Tests.ScanComments/"'
371+
```
372+
373+
Or to run only a specific test case:
374+
375+
```shell
376+
stack test sslang:scanner-test --ta '--match "/Tests.ScanComments/ignores single-line comments/"'
377+
```
378+
379+
[hspec]: http://hspec.github.io/
380+
[hspec-discover]: http://hspec.github.io/hspec-discover.html
381+
382+
### Linting and Formatting
383+
384+
To keep code on the main branch clean and consistent, you should always make sure to lint (with [Hlint][hlint]) and format (with [Brittany][brittany]) your code before merging any PR. You may invoke Hlint and Brittany manually, but the following [convenience aliases][convenience-aliases] are provided to coordinate with your development workflow.
385+
386+
To lint:
387+
388+
```shell
389+
git lint # lint all files modified since HEAD
390+
git lint [<files..>] # lint specified files
391+
git lint --since <commit> # lint all files modified since <commit>
392+
git lint --help # show help menu
393+
```
394+
395+
To format:
396+
397+
```shell
398+
git fmt # format all files modified since HEAD
399+
git fmt [<files..>] # format specified files
400+
git fmt --since <commit> # format all files modified since <commit>
401+
git fmt --help # show help menu
402+
```

0 commit comments

Comments
 (0)