Skip to content

Commit 2986c5d

Browse files
authored
Support for umbrella projects via CLI (#2089)
1 parent 4bc56b9 commit 2986c5d

File tree

5 files changed

+83
-24
lines changed

5 files changed

+83
-24
lines changed

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@ You can use ExDoc via the command line.
114114
GITHUB_REPO => ecto
115115
```
116116

117+
It is also possible to specify multiple `ebin` directories in the case of _umbrella_ projects:
118+
119+
```bash
120+
$ ex_doc "PROJECT_NAME" "PROJECT_VERSION" _build/dev/lib/app1/ebin _build/dev/lib/app2/ebin -m "PROJECT_MODULE" -u "https://github.com/GITHUB_USER/GITHUB_REPO" -l path/to/logo.png
121+
```
122+
123+
If multiple `ebin` directories are specified, modules are grouped by application by default. It is possible to override this behaviour by providing a custom `groups_per_modules` option.
124+
117125
You can specify a config file via the `--config` option, both Elixir and Erlang formats are supported. Invoke `ex_doc` without arguments to learn more.
118126

119127
<!-- tabs-close -->

lib/ex_doc/cli.ex

+5-11
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ defmodule ExDoc.CLI do
6969
end
7070

7171
defp generate(args, opts, generator) do
72-
[project, version, source_beam] = parse_args(args)
72+
[project, version | source_beams] = parse_args(args)
7373

74-
Code.prepend_path(source_beam)
74+
Code.prepend_paths(source_beams)
7575

7676
for path <- Keyword.get_values(opts, :paths),
7777
path <- Path.wildcard(path) do
@@ -80,8 +80,8 @@ defmodule ExDoc.CLI do
8080

8181
opts =
8282
opts
83-
|> Keyword.put(:source_beam, source_beam)
84-
|> Keyword.put(:apps, [app(source_beam)])
83+
|> Keyword.put(:source_beam, source_beams)
84+
|> Keyword.put(:apps, Enum.map(source_beams, &app/1))
8585
|> merge_config()
8686
|> normalize_formatters()
8787

@@ -166,13 +166,7 @@ defmodule ExDoc.CLI do
166166
end
167167
end
168168

169-
defp parse_args([_project, _version, _source_beam] = args), do: args
170-
171-
defp parse_args([_, _, _ | _]) do
172-
IO.puts("Too many arguments.\n")
173-
print_usage()
174-
exit({:shutdown, 1})
175-
end
169+
defp parse_args([_project, _version | _source_beams] = args), do: args
176170

177171
defp parse_args(_) do
178172
IO.puts("Too few arguments.\n")

lib/ex_doc/config.ex

+15-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,10 @@ defmodule ExDoc.Config do
115115

116116
{groups_for_docs, options} = Keyword.pop(options, :groups_for_docs, [])
117117
{groups_for_extras, options} = Keyword.pop(options, :groups_for_extras, [])
118-
{groups_for_modules, options} = Keyword.pop(options, :groups_for_modules, [])
118+
apps = Keyword.get(options, :apps, [])
119+
120+
{groups_for_modules, options} =
121+
Keyword.pop(options, :groups_for_modules, default_groups_for_modules(apps))
119122

120123
{skip_undefined_reference_warnings_on, options} =
121124
Keyword.pop(
@@ -278,4 +281,15 @@ defmodule ExDoc.Config do
278281
defp append_slash(url) do
279282
if :binary.last(url) == ?/, do: url, else: url <> "/"
280283
end
284+
285+
defp default_groups_for_modules([_app]) do
286+
[]
287+
end
288+
289+
defp default_groups_for_modules(apps) do
290+
Enum.map(apps, fn app ->
291+
Application.load(app)
292+
{app, Application.spec(app, :modules)}
293+
end)
294+
end
281295
end

test/ex_doc/cli_test.exs

+17-12
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ defmodule ExDoc.CLITest do
33
import ExUnit.CaptureIO
44

55
@ebin "_build/test/lib/ex_doc/ebin"
6+
@ebin2 "_build/test/lib/makeup/ebin"
67

78
defp run(args) do
89
with_io(fn -> ExDoc.CLI.main(args, &{&1, &2, &3}) end)
@@ -17,7 +18,7 @@ defmodule ExDoc.CLITest do
1718
formatter: "html",
1819
formatters: ["html", "epub"],
1920
apps: [:ex_doc],
20-
source_beam: @ebin
21+
source_beam: [@ebin]
2122
]}
2223

2324
assert epub ==
@@ -26,7 +27,7 @@ defmodule ExDoc.CLITest do
2627
formatter: "epub",
2728
formatters: ["html", "epub"],
2829
apps: [:ex_doc],
29-
source_beam: @ebin
30+
source_beam: [@ebin]
3031
]}
3132
end
3233

@@ -39,7 +40,7 @@ defmodule ExDoc.CLITest do
3940
formatter: "epub",
4041
formatters: ["epub", "html"],
4142
apps: [:ex_doc],
42-
source_beam: @ebin
43+
source_beam: [@ebin]
4344
]}
4445

4546
assert html ==
@@ -48,7 +49,7 @@ defmodule ExDoc.CLITest do
4849
formatter: "html",
4950
formatters: ["epub", "html"],
5051
apps: [:ex_doc],
51-
source_beam: @ebin
52+
source_beam: [@ebin]
5253
]}
5354
end
5455

@@ -60,14 +61,18 @@ defmodule ExDoc.CLITest do
6061
assert io == "ExDoc v#{ExDoc.version()}\n"
6162
end
6263

63-
test "too many arguments" do
64-
assert catch_exit(run(["ExDoc", "1.2.3", "/", "kaboom"])) == {:shutdown, 1}
65-
end
66-
6764
test "too few arguments" do
6865
assert catch_exit(run(["ExDoc"])) == {:shutdown, 1}
6966
end
7067

68+
test "multiple apps" do
69+
{[{"ExDoc", "1.2.3", html}, {"ExDoc", "1.2.3", epub}], _io} =
70+
run(["ExDoc", "1.2.3", @ebin, @ebin2])
71+
72+
assert [:ex_doc, :makeup] = Enum.sort(Keyword.get(html, :apps))
73+
assert [:ex_doc, :makeup] = Enum.sort(Keyword.get(epub, :apps))
74+
end
75+
7176
test "arguments that are not aliased" do
7277
File.write!("not_aliased.exs", ~s([key: "val"]))
7378

@@ -98,7 +103,7 @@ defmodule ExDoc.CLITest do
98103
logo: "logo.png",
99104
main: "Main",
100105
output: "html",
101-
source_beam: "#{@ebin}",
106+
source_beam: ["#{@ebin}"],
102107
source_ref: "abcdefg",
103108
source_url: "http://example.com/username/project"
104109
]
@@ -127,7 +132,7 @@ defmodule ExDoc.CLITest do
127132
extras: ["README.md"],
128133
formatter: "html",
129134
formatters: ["html"],
130-
source_beam: @ebin
135+
source_beam: [@ebin]
131136
]
132137
after
133138
File.rm!("test.exs")
@@ -155,7 +160,7 @@ defmodule ExDoc.CLITest do
155160
formatter: "html",
156161
formatters: ["html"],
157162
logo: "opts_logo.png",
158-
source_beam: @ebin
163+
source_beam: [@ebin]
159164
]
160165
after
161166
File.rm!("test.exs")
@@ -192,7 +197,7 @@ defmodule ExDoc.CLITest do
192197
extras: ["README.md"],
193198
formatter: "html",
194199
formatters: ["html"],
195-
source_beam: @ebin
200+
source_beam: [@ebin]
196201
]
197202
after
198203
File.rm!("test.config")

test/ex_doc/config_test.exs

+38
Original file line numberDiff line numberDiff line change
@@ -113,5 +113,43 @@ defmodule ExDoc.ConfigTest do
113113
config = build(source_url_pattern: "a%{line}b%{path}c")
114114
assert config.source_url_pattern.("foo.ex", 123) == "a123bfoo.exc"
115115
end
116+
117+
test "groups_for_modules" do
118+
# Using real applications, since we load them to extract the corresponding list of modules
119+
stdlib = :stdlib
120+
kernel = :kernel
121+
custom_group = :custom_group
122+
123+
groups_for_modules = fn config, key ->
124+
List.keyfind(config.groups_for_modules, to_string(key), 0)
125+
end
126+
127+
# Single app, no custom grouping
128+
config = build(apps: [stdlib])
129+
assert groups_for_modules.(config, stdlib) == nil
130+
assert groups_for_modules.(config, custom_group) == nil
131+
132+
# Single app, custom grouping
133+
config = build(apps: [stdlib], groups_for_modules: [{"custom_group", ["module_1"]}])
134+
assert groups_for_modules.(config, stdlib) == nil
135+
assert groups_for_modules.(config, custom_group) == {"custom_group", ["module_1"]}
136+
137+
# Multiple apps, no custom grouping
138+
config = build(apps: [stdlib, kernel])
139+
stdlib_groups = groups_for_modules.(config, stdlib)
140+
kernel_groups = groups_for_modules.(config, kernel)
141+
assert match?({"stdlib", _}, stdlib_groups)
142+
assert match?({"kernel", _}, kernel_groups)
143+
{"stdlib", stdlib_modules} = stdlib_groups
144+
{"kernel", kernel_modules} = kernel_groups
145+
assert Enum.member?(stdlib_modules, :gen_server)
146+
assert Enum.member?(kernel_modules, :file)
147+
148+
# Multiple apps, custom grouping
149+
config = build(apps: [stdlib, kernel], groups_for_modules: [{"custom_group", ["module_1"]}])
150+
assert groups_for_modules.(config, stdlib) == nil
151+
assert groups_for_modules.(config, kernel) == nil
152+
assert groups_for_modules.(config, custom_group) == {"custom_group", ["module_1"]}
153+
end
116154
end
117155
end

0 commit comments

Comments
 (0)