Skip to content

Commit 67cbe5b

Browse files
committed
Merge branch 'stable'
2 parents 45a8ec7 + b521d2d commit 67cbe5b

File tree

1 file changed

+116
-56
lines changed

1 file changed

+116
-56
lines changed

doc/tutorial/multi-package_projects.md

Lines changed: 116 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,91 +2,151 @@
22

33
# 9. Multi-package projects
44

5-
Until now, everything we have done with Stack has used a single-package project.
6-
However, Stack's power truly shines when you are working on multi-package
7-
projects. All the functionality you'd expect to work just does: dependencies
8-
between packages are detected and respected, dependencies of all packages are
9-
just as one cohesive whole, and if anything fails to build, the build commands
10-
exits appropriately.
5+
Everything we have done with Stack so far has used a single-package project,
6+
where the project directory is also the package's directory. However, a Stack
7+
project can have more than one project package.
118

12-
Let us demonstrate this with the `wai-app-static` and `yackage` packages,
13-
starting in the root directory for all our Haskell projects. Command:
14-
15-
~~~text
16-
mkdir multi
17-
cd multi
18-
stack unpack wai-app-static yackage
19-
~~~
20-
21-
The last command should report something like:
22-
23-
~~~text
24-
...
25-
Unpacked wai-app-static (from Hackage) to .../multi/wai-app-static-3.1.9/.
26-
Unpacked yackage (from Hackage) to .../multi/yackage-0.8.1/.
27-
~~~
28-
29-
Then command:
9+
Let us demonstrate this with a project that has two project packages named
10+
`packageA` and `packageB`. We will create a project directory named `my-project`
11+
and, for our example, create the two project packages in subdirectories.
12+
Command:
3013

3114
~~~text
15+
mkdir my-project
16+
cd my-project
17+
stack new packageA --no-init
18+
stack new packageB --no-init
3219
stack init
3320
~~~
3421

35-
The command should report something like:
22+
The `--no-init` flags above stop Stack from creating project-level configuration
23+
files in the `packageA` and `packageB` directories that
24+
[`stack new`](../commands/new_command.md) will create.
25+
26+
The [`stack init`](../commands/init_command.md) command above creates a
27+
project-level configuration file (`stack.yaml`) in the `my-project` directory.
28+
The command should report something like this:
3629

3730
~~~text
3831
Looking for Cabal or package.yaml files to use to initialise Stack's
39-
project-level configuration file.
32+
project-level YAML configuration file.
4033
4134
Using the Cabal packages:
42-
* wai-app-static-3.1.9/
43-
* yackage-0.8.1/
35+
* packageA\
36+
* packageB\
4437
45-
Cabal file warning in .../multi/yackage-0.8.1/yackage.cabal@47:40: version
46-
operators used. To use version operators the package needs to specify at least
47-
'cabal-version: >= 1.8'.
48-
Cabal file warning in .../multi/yackage-0.8.1/yackage.cabal@21:36: version
49-
operators used. To use version operators the package needs to specify at least
50-
'cabal-version: >= 1.8'.
51-
Selecting the best among 12 snapshots...
38+
Selecting the best among 14 snapshots...
5239
53-
Note: Matches ...
40+
Note: Matches https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/24/25.yaml
5441
55-
Selected the snapshot ...
56-
Initialising Stack's project-level configuration file using snapshot ...
42+
Selected the snapshot https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/24/25.yaml.
43+
Initialising Stack's project-level configuration file using snapshot https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/24/25.yaml.
5744
Considered 2 user packages.
5845
Writing configuration to stack.yaml.
5946
Stack's project-level configuration file has been initialised.
6047
~~~
6148

62-
Then command:
49+
Ignoring comments in the file, the content of the created `stack.yaml` file
50+
should be something like this:
51+
52+
~~~yaml
53+
snapshot:
54+
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/24/25.yaml
55+
56+
packages:
57+
- packageA
58+
- packageB
59+
~~~
60+
61+
The value of the [`packages`](../configure/yaml/project.md#packages) key is a
62+
list of paths (relative paths, in this example) to project package directories.
63+
64+
If we command
65+
[`stack ide targets`](../commands/ide_command.md#the-stack-ide-targets-command),
66+
Stack reports the build targets for these two project packages:
6367

6468
~~~text
65-
stack build --haddock --test
69+
packageA:lib
70+
packageA:exe:packageA-exe
71+
packageA:test:packageA-test
72+
packageB:lib
73+
packageB:exe:packageB-exe
74+
packageB:test:packageB-test
6675
~~~
6776

68-
Stack should build and test the project packages.
77+
If we command
78+
[`stack build`](../commands/build_command.md#no-targets-specified), Stack will
79+
build all the library and executable components of all the project packages.
6980

70-
If you look at the `stack.yaml` file, you will see exactly what you'd expect:
81+
One project package can depend on another. Let us demonstrate this by modifying
82+
the main library of the `packageB` package to depend on that of the `packageA`
83+
package.
84+
85+
Currently, the source code of the `packageA` and `packageB` packages are the
86+
same. Let us first modify the `someFunc` function exported by the `Lib` module
87+
exposed by the `packageA` package, as follows:
88+
89+
~~~haskell
90+
someFunc :: IO ()
91+
someFunc = putStrLn "someFunc of packageA's Lib module"
92+
~~~
93+
94+
and the source code of the `Lib` module exposed by the `packageB` package to
95+
become:
96+
97+
~~~haskell
98+
{-# LANGUAGE PackageImports #-}
99+
100+
module Lib
101+
( someFunc
102+
) where
103+
104+
import qualified "packageA" Lib as LibA
105+
106+
someFunc :: IO ()
107+
someFunc = do
108+
putStrLn "someFunc of packageB's Lib module"
109+
LibA.someFunc
110+
~~~
111+
112+
In this example, as the `packageA` and `packageB` packages both expose a module
113+
named `Lib`, we have to use GHC's language extension
114+
[`PackageImports`](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/package_qualified_imports.html)
115+
to allow imports from the `Lib` module exposed by the `packageA` package to be
116+
distiguished.
117+
118+
In the package description file (`package.yaml`) for the `packageB` package, we
119+
need to specify that the dependencies of its main library now include the main
120+
library of the `packageA` package, as follows (extract):
71121

72122
~~~yaml
73-
snapshot:
74-
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/31.yaml
75-
packages:
76-
- wai-app-static-3.1.9
77-
- yackage-0.8.1
123+
library:
124+
source-dirs: src
125+
dependencies:
126+
- packageA # Add the dependency on the main library of the packageA package
78127
~~~
79128

80-
Notice that multiple directories are listed in the `packages` key.
129+
Now, if we command `stack build packageB`, Stack will build the library and
130+
executable components of the `packageA` package (the dependency) and then the
131+
library and executable (named `packageB-exe`) of `packageB`.
132+
133+
To execute the built `packageB-exe` executable, we can command:
81134

82-
In addition to local directories, you can also refer to packages available in a
83-
Git repository or in a tarball over HTTP/HTTPS. This can be useful for using a
84-
modified version of a dependency that has not yet been released upstream.
135+
~~~text
136+
stack exec packageB-exe
137+
~~~
138+
139+
giving the expected output:
140+
141+
~~~text
142+
someFunc of packageB's Lib module
143+
someFunc of packageA's Lib module
144+
~~~
85145

86146
!!! note
87147

88-
When adding upstream packages directly to your project it is important to
89-
distinguish _project packages_ located locally from the upstream
90-
_dependency packages_. Otherwise you may have trouble running `stack ghci`.
91-
See [stack.yaml documentation](../configure/yaml/project.md#packages) for
92-
more details.
148+
A project package can depend on another project package, as above. It can
149+
also depend on a local package that is specified as an
150+
[extra-dep](../configure/yaml/project.md#extra-deps). Although both
151+
dependencies are local, the former is part of the project and the latter is
152+
not.

0 commit comments

Comments
 (0)